<commit_before>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/common/extensions/permissions/media_galleries_permission.h"

#include <algorithm>

#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/stl_util.h"
#include "base/values.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/extension_messages.h"
#include "chrome/common/extensions/permissions/api_permission.h"
#include "chrome/common/extensions/permissions/permission_message.h"
#include "chrome/common/extensions/permissions/permissions_info.h"
#include "grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"

namespace {

const char kAllAutoDetectedString[] = "allAutoDetected";
const char kReadString[] = "read";
const char kInvalidString[] = "invalid";

}  // namespace

namespace extensions {

MediaGalleriesPermission::MediaGalleriesPermission(
    const APIPermissionInfo* info)
    : APIPermission(info) {
}

MediaGalleriesPermission::~MediaGalleriesPermission() {
}

// static
bool MediaGalleriesPermission::HasReadAccess(const Extension& extension) {
  CheckParam read_param(kRead);
  return extension.CheckAPIPermissionWithParam(
      extensions::APIPermission::kMediaGalleries, &read_param);
}

// static
bool MediaGalleriesPermission::HasAllGalleriesAccess(
    const Extension& extension) {
  CheckParam all_param(kAllAutoDetected);
  return extension.CheckAPIPermissionWithParam(
      extensions::APIPermission::kMediaGalleries, &all_param);
}

// static
MediaGalleriesPermission::PermissionTypes
MediaGalleriesPermission::PermissionStringToType(const std::string& str) {
  if (str == kAllAutoDetectedString)
    return kAllAutoDetected;
  if (str == kReadString)
    return kRead;
  return kNone;
}

// static
const char* MediaGalleriesPermission::PermissionTypeToString(
    PermissionTypes type) {
  switch (type) {
    case kAllAutoDetected:
      return kAllAutoDetectedString;
    case kRead:
      return kReadString;
    default:
      NOTREACHED();
      return kInvalidString;
  }
}

bool MediaGalleriesPermission::HasMessages() const {
  CheckParam param(kAllAutoDetected);
  return Check(&param);
}

PermissionMessages MediaGalleriesPermission::GetMessages() const {
  CheckParam param(kAllAutoDetected);
  if (Check(&param)) {
    PermissionMessages result;
    result.push_back(PermissionMessage(
        PermissionMessage::kMediaGalleriesAllGalleries,
        l10n_util::GetStringUTF16(
            IDS_EXTENSION_PROMPT_WARNING_MEDIA_GALLERIES_ALL_GALLERIES)));
    return result;
  }
  NOTREACHED();
  PermissionMessages result;
  result.push_back(PermissionMessage(PermissionMessage::kUnknown, string16()));
  return result;
}

bool MediaGalleriesPermission::Check(
    const APIPermission::CheckParam* param) const {
  const CheckParam* media_galleries_param =
      static_cast<const CheckParam*>(param);
  return ContainsKey(permissions_, media_galleries_param->permission);
}

bool MediaGalleriesPermission::Contains(const APIPermission* rhs) const {
  CHECK_EQ(rhs->info(), info());
  const MediaGalleriesPermission* perm =
      static_cast<const MediaGalleriesPermission*>(rhs);
  return std::includes(permissions_.begin(), permissions_.end(),
                       perm->permissions_.begin(), perm->permissions_.end());
}

bool MediaGalleriesPermission::Equal(const APIPermission* rhs) const {
  CHECK(rhs->info() == info());
  const MediaGalleriesPermission* perm =
      static_cast<const MediaGalleriesPermission*>(rhs);
  return permissions_ == perm->permissions_;
}

bool MediaGalleriesPermission::FromValue(const base::Value* value) {
  permissions_.clear();

  if (!value)
    return false;

  const base::ListValue* list = NULL;
  if (!value->GetAsList(&list) || list->GetSize() == 0)
    return false;

  for (size_t i = 0; i < list->GetSize(); ++i) {
    std::string str;
    if (!list->GetString(i, &str))
      return false;
    PermissionTypes type = PermissionStringToType(str);
    if (type == kNone)
      return false;
    permissions_.insert(type);
  }
  return true;
}

void MediaGalleriesPermission::ToValue(base::Value** value) const {
  base::ListValue* list = new ListValue();

  for (std::set<PermissionTypes>::const_iterator it = permissions_.begin();
       it != permissions_.end();
       ++it) {
    list->Append(base::Value::CreateStringValue(PermissionTypeToString(*it)));
  }
  *value = list;
}

APIPermission* MediaGalleriesPermission::Clone() const {
  MediaGalleriesPermission* result = new MediaGalleriesPermission(info());
  result->permissions_ = permissions_;
  return result;
}

APIPermission* MediaGalleriesPermission::Diff(const APIPermission* rhs) const {
  CHECK(rhs->info() == info());
  const MediaGalleriesPermission* perm =
      static_cast<const MediaGalleriesPermission*>(rhs);
  scoped_ptr<MediaGalleriesPermission> result(
      new MediaGalleriesPermission(info()));
  std::set_difference(permissions_.begin(), permissions_.end(),
                      perm->permissions_.begin(), perm->permissions_.end(),
                      std::inserter<std::set<PermissionTypes> >(
                          result->permissions_, result->permissions_.begin()));
  return result->permissions_.empty() ? NULL : result.release();
}

APIPermission* MediaGalleriesPermission::Union(const APIPermission* rhs) const {
  CHECK(rhs->info() == info());
  const MediaGalleriesPermission* perm =
      static_cast<const MediaGalleriesPermission*>(rhs);
  scoped_ptr<MediaGalleriesPermission> result(
      new MediaGalleriesPermission(info()));
  std::set_union(permissions_.begin(), permissions_.end(),
                 perm->permissions_.begin(), perm->permissions_.end(),
                 std::inserter<std::set<PermissionTypes> >(
                     result->permissions_, result->permissions_.begin()));
  return result->permissions_.empty() ? NULL : result.release();
}

APIPermission* MediaGalleriesPermission::Intersect(
    const APIPermission* rhs) const {
  CHECK(rhs->info() == info());
  const MediaGalleriesPermission* perm =
      static_cast<const MediaGalleriesPermission*>(rhs);
  scoped_ptr<MediaGalleriesPermission> result(
      new MediaGalleriesPermission(info()));
  std::set_intersection(permissions_.begin(), permissions_.end(),
                        perm->permissions_.begin(), perm->permissions_.end(),
                        std::inserter<std::set<PermissionTypes> >(
                            result->permissions_,
                            result->permissions_.begin()));
  return result->permissions_.empty() ? NULL : result.release();
}

void MediaGalleriesPermission::Write(IPC::Message* m) const {
  IPC::WriteParam(m, permissions_);
}

bool MediaGalleriesPermission::Read(const IPC::Message* m,
                                    PickleIterator* iter) {
  return IPC::ReadParam(m, iter, &permissions_);
}

void MediaGalleriesPermission::Log(std::string* log) const {
  IPC::LogParam(permissions_, log);
}

}  // namespace extensions
<commit_msg>Temporarily add back all-auto-detected as an alias for allAutoDetected to make transition easier.<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/common/extensions/permissions/media_galleries_permission.h"

#include <algorithm>

#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/stl_util.h"
#include "base/values.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/extension_messages.h"
#include "chrome/common/extensions/permissions/api_permission.h"
#include "chrome/common/extensions/permissions/permission_message.h"
#include "chrome/common/extensions/permissions/permissions_info.h"
#include "grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"

namespace {

const char kAllAutoDetectedString[] = "allAutoDetected";
const char kAllAutoDetectedAliasString[] = "all-auto-detected";
const char kReadString[] = "read";
const char kInvalidString[] = "invalid";

}  // namespace

namespace extensions {

MediaGalleriesPermission::MediaGalleriesPermission(
    const APIPermissionInfo* info)
    : APIPermission(info) {
}

MediaGalleriesPermission::~MediaGalleriesPermission() {
}

// static
bool MediaGalleriesPermission::HasReadAccess(const Extension& extension) {
  CheckParam read_param(kRead);
  return extension.CheckAPIPermissionWithParam(
      extensions::APIPermission::kMediaGalleries, &read_param);
}

// static
bool MediaGalleriesPermission::HasAllGalleriesAccess(
    const Extension& extension) {
  CheckParam all_param(kAllAutoDetected);
  return extension.CheckAPIPermissionWithParam(
      extensions::APIPermission::kMediaGalleries, &all_param);
}

// static
MediaGalleriesPermission::PermissionTypes
MediaGalleriesPermission::PermissionStringToType(const std::string& str) {
  if (str == kAllAutoDetectedString)
    return kAllAutoDetected;
  if (str == kAllAutoDetectedAliasString)
    return kAllAutoDetected;
  if (str == kReadString)
    return kRead;
  return kNone;
}

// static
const char* MediaGalleriesPermission::PermissionTypeToString(
    PermissionTypes type) {
  switch (type) {
    case kAllAutoDetected:
      return kAllAutoDetectedString;
    case kRead:
      return kReadString;
    default:
      NOTREACHED();
      return kInvalidString;
  }
}

bool MediaGalleriesPermission::HasMessages() const {
  CheckParam param(kAllAutoDetected);
  return Check(&param);
}

PermissionMessages MediaGalleriesPermission::GetMessages() const {
  CheckParam param(kAllAutoDetected);
  if (Check(&param)) {
    PermissionMessages result;
    result.push_back(PermissionMessage(
        PermissionMessage::kMediaGalleriesAllGalleries,
        l10n_util::GetStringUTF16(
            IDS_EXTENSION_PROMPT_WARNING_MEDIA_GALLERIES_ALL_GALLERIES)));
    return result;
  }
  NOTREACHED();
  PermissionMessages result;
  result.push_back(PermissionMessage(PermissionMessage::kUnknown, string16()));
  return result;
}

bool MediaGalleriesPermission::Check(
    const APIPermission::CheckParam* param) const {
  const CheckParam* media_galleries_param =
      static_cast<const CheckParam*>(param);
  return ContainsKey(permissions_, media_galleries_param->permission);
}

bool MediaGalleriesPermission::Contains(const APIPermission* rhs) const {
  CHECK_EQ(rhs->info(), info());
  const MediaGalleriesPermission* perm =
      static_cast<const MediaGalleriesPermission*>(rhs);
  return std::includes(permissions_.begin(), permissions_.end(),
                       perm->permissions_.begin(), perm->permissions_.end());
}

bool MediaGalleriesPermission::Equal(const APIPermission* rhs) const {
  CHECK(rhs->info() == info());
  const MediaGalleriesPermission* perm =
      static_cast<const MediaGalleriesPermission*>(rhs);
  return permissions_ == perm->permissions_;
}

bool MediaGalleriesPermission::FromValue(const base::Value* value) {
  permissions_.clear();

  if (!value)
    return false;

  const base::ListValue* list = NULL;
  if (!value->GetAsList(&list) || list->GetSize() == 0)
    return false;

  for (size_t i = 0; i < list->GetSize(); ++i) {
    std::string str;
    if (!list->GetString(i, &str))
      return false;
    PermissionTypes type = PermissionStringToType(str);
    if (type == kNone)
      return false;
    permissions_.insert(type);
  }
  return true;
}

void MediaGalleriesPermission::ToValue(base::Value** value) const {
  base::ListValue* list = new ListValue();

  for (std::set<PermissionTypes>::const_iterator it = permissions_.begin();
       it != permissions_.end();
       ++it) {
    list->Append(base::Value::CreateStringValue(PermissionTypeToString(*it)));
  }
  *value = list;
}

APIPermission* MediaGalleriesPermission::Clone() const {
  MediaGalleriesPermission* result = new MediaGalleriesPermission(info());
  result->permissions_ = permissions_;
  return result;
}

APIPermission* MediaGalleriesPermission::Diff(const APIPermission* rhs) const {
  CHECK(rhs->info() == info());
  const MediaGalleriesPermission* perm =
      static_cast<const MediaGalleriesPermission*>(rhs);
  scoped_ptr<MediaGalleriesPermission> result(
      new MediaGalleriesPermission(info()));
  std::set_difference(permissions_.begin(), permissions_.end(),
                      perm->permissions_.begin(), perm->permissions_.end(),
                      std::inserter<std::set<PermissionTypes> >(
                          result->permissions_, result->permissions_.begin()));
  return result->permissions_.empty() ? NULL : result.release();
}

APIPermission* MediaGalleriesPermission::Union(const APIPermission* rhs) const {
  CHECK(rhs->info() == info());
  const MediaGalleriesPermission* perm =
      static_cast<const MediaGalleriesPermission*>(rhs);
  scoped_ptr<MediaGalleriesPermission> result(
      new MediaGalleriesPermission(info()));
  std::set_union(permissions_.begin(), permissions_.end(),
                 perm->permissions_.begin(), perm->permissions_.end(),
                 std::inserter<std::set<PermissionTypes> >(
                     result->permissions_, result->permissions_.begin()));
  return result->permissions_.empty() ? NULL : result.release();
}

APIPermission* MediaGalleriesPermission::Intersect(
    const APIPermission* rhs) const {
  CHECK(rhs->info() == info());
  const MediaGalleriesPermission* perm =
      static_cast<const MediaGalleriesPermission*>(rhs);
  scoped_ptr<MediaGalleriesPermission> result(
      new MediaGalleriesPermission(info()));
  std::set_intersection(permissions_.begin(), permissions_.end(),
                        perm->permissions_.begin(), perm->permissions_.end(),
                        std::inserter<std::set<PermissionTypes> >(
                            result->permissions_,
                            result->permissions_.begin()));
  return result->permissions_.empty() ? NULL : result.release();
}

void MediaGalleriesPermission::Write(IPC::Message* m) const {
  IPC::WriteParam(m, permissions_);
}

bool MediaGalleriesPermission::Read(const IPC::Message* m,
                                    PickleIterator* iter) {
  return IPC::ReadParam(m, iter, &permissions_);
}

void MediaGalleriesPermission::Log(std::string* log) const {
  IPC::LogParam(permissions_, log);
}

}  // namespace extensions
<|endoftext|>


<commit_before>/*
* Copyright (C) 2015 German Aerospace Center (DLR/SC)
*
* Created: 2015-10-20 Martin Siggel <Martin.Siggel@dlr.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "CTiglWingBuilder.h"

#include "CTiglMakeLoft.h"
#include "CCPACSWingSegment.h"
#include "CTiglLogging.h"
#include "CNamedShape.h"
#include "tiglcommonfunctions.h"

#include <TopExp.hxx>
#include <TopoDS_Iterator.hxx>
#include <TopTools_IndexedMapOfShape.hxx>
#include <BRepBuilderAPI_MakeWire.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include <BRepOffsetAPI_ThruSections.hxx>
#include <BRepBuilderAPI_Sewing.hxx>
#include <BRepClass3d_SolidClassifier.hxx>
#include <BRep_Builder.hxx>
#include <BRepBuilderAPI_FindPlane.hxx>
#include <BRepBuilderAPI_MakeFace.hxx>
#include <BRepLib.hxx>
#include <Geom_Plane.hxx>

#include <cassert>

#define NO_EXPLICIT_TE_MODELING

namespace tigl
{

Standard_Boolean CreateSideCap(const TopoDS_Wire& W,
                               const Standard_Real presPln,
                               TopoDS_Face& theFace);

CTiglWingBuilder::CTiglWingBuilder(const CCPACSWing& wing)
    : _wing(wing)
{
}

#ifndef NO_EXPLICIT_TE_MODELING
PNamedShape CTiglWingBuilder::BuildShape()
{
    const CCPACSWingSegments& segments = _wing.m_segments;

    // check whether we have a blunt TE or not
    const CCPACSWingProfile& innerProfile = segments.GetSegment(1).GetInnerConnection().GetProfile();

    TopoDS_Compound guideCurves = _wing.GetGuideCurveWires();

    std::vector<TopoDS_Wire> guides;
    for (TopoDS_Iterator anIter(guideCurves); anIter.More(); anIter.Next()) {
        TopoDS_Wire aSh = TopoDS::Wire(anIter.Value());
        guides.push_back(aSh);
    }

    // we assume, that all profiles of one wing are either blunt or not
    // this is checked during cpacs loading of each wing segment
    bool hasBluntTE = innerProfile.HasBluntTE();
    bool hasGuideCurves = guides.size() > 0;

    CTiglMakeLoft lofter;
    lofter.setMakeSolid(false);

    std::vector<gp_Pnt> upperTEPoints, lowerTEPoints;
    for (int i=1; i <= segments.GetSegmentCount(); i++) {
        const TopoDS_Wire& startWire = segments.GetSegment(i).GetInnerWire();

        // extract trailing edge, in case of a blunt TE
        // we assume, that the TE is the last edge of the wire
        if (hasBluntTE) {
            BRepBuilderAPI_MakeWire wireMaker;
            TopTools_IndexedMapOfShape edgeMap;
            TopExp::MapShapes(startWire, TopAbs_EDGE, edgeMap);
            for (int i = 1; i <= edgeMap.Extent(); ++i) {
                const TopoDS_Edge& e = TopoDS::Edge(edgeMap(i));
                if (i < edgeMap.Extent()) {
                    wireMaker.Add(e);
                }
            }
            TopoDS_Wire aeroProfile = wireMaker.Wire();
            lofter.addProfiles(aeroProfile);
            upperTEPoints.push_back(GetLastPoint(aeroProfile));
            lowerTEPoints.push_back(GetFirstPoint(aeroProfile));
        }
        else {
            lofter.addProfiles(startWire);
        }
    }

    TopoDS_Wire endWire =  segments.GetSegment(segments.GetSegmentCount()).GetOuterWire();
    if (hasBluntTE) {
        // extract the wire without the trailing edge

        BRepBuilderAPI_MakeWire wireMaker;
        TopTools_IndexedMapOfShape edgeMap;
        TopExp::MapShapes(endWire, TopAbs_EDGE, edgeMap);
        for (int i = 1; i <= edgeMap.Extent(); ++i) {
            const TopoDS_Edge& e = TopoDS::Edge(edgeMap(i));
            if (i < edgeMap.Extent()) {
                wireMaker.Add(e);
            }
        }
        TopoDS_Wire aeroProfile = wireMaker.Wire();
        lofter.addProfiles(aeroProfile);
        upperTEPoints.push_back(GetLastPoint(aeroProfile));
        lowerTEPoints.push_back(GetFirstPoint(aeroProfile));
    }
    else {
        lofter.addProfiles(endWire);
    }

    // add guide curves
    lofter.addGuides(guideCurves);

    TopoDS_Shape aeroShape = lofter.Shape();
    BRepBuilderAPI_Sewing sewingAlgo;
    sewingAlgo.Add(aeroShape);

    // If blunt trailing edge is available, model it explicitly
    if (hasBluntTE) {

        TopoDS_Wire upperTE, lowerTE;
        if (hasGuideCurves) {
            // The guide curves are sorted in the order of the relative starting coordinate
            // at the innermost section
            upperTE = guides.back();
            lowerTE = guides.front();
        }
        else {
            // model the edges by taking the first and last profile points

            assert(lowerTEPoints.size() == upperTEPoints.size());
            assert(lowerTEPoints.size() > 1);

            // build upper edge
            BRepBuilderAPI_MakeWire upperWireMaker, lowerWireMaker;
            for (unsigned int i = 0; i < upperTEPoints.size() - 1; ++i) {
                upperWireMaker.Add(BRepBuilderAPI_MakeEdge(upperTEPoints.at(i), upperTEPoints.at(i+1)));
                lowerWireMaker.Add(BRepBuilderAPI_MakeEdge(lowerTEPoints.at(i), lowerTEPoints.at(i+1)));
            }
            upperTE = upperWireMaker.Wire();
            lowerTE = lowerWireMaker.Wire();
        }

        // the TE is build using a ruled loft
        BRepOffsetAPI_ThruSections trailingEdgeBuilder(Standard_False, Standard_True);
        trailingEdgeBuilder.AddWire(upperTE);
        trailingEdgeBuilder.AddWire(lowerTE);
        trailingEdgeBuilder.Build();
        TopoDS_Shape trailingEdge = trailingEdgeBuilder.Shape();
        sewingAlgo.Add(trailingEdge);

    } // hasBluntTE

    // get the profiles
    TopoDS_Wire innerWire = segments.GetSegment(1).GetInnerWire();
    TopoDS_Wire outerWire = segments.GetSegment(segments.GetSegmentCount()).GetOuterWire();

    TopoDS_Face innerFace, outerFace;
    CreateSideCap(innerWire, 1e-6, innerFace);
    CreateSideCap(outerWire, 1e-6, outerFace);

    sewingAlgo.Add(innerFace);
    sewingAlgo.Add(outerFace);

    sewingAlgo.Perform();
    TopoDS_Shape shellClosed  = sewingAlgo.SewedShape();
    shellClosed.Closed(Standard_True);

    // make solid from shell
    TopoDS_Solid solid;
    BRep_Builder solidMaker;
    solidMaker.MakeSolid(solid);
    solidMaker.Add(solid, shellClosed);

    // verify the orientation the solid
    BRepClass3d_SolidClassifier clas3d(solid);
    clas3d.PerformInfinitePoint(Precision::Confusion());
    if (clas3d.State() == TopAbs_IN) {
     solidMaker.MakeSolid(solid);
        TopoDS_Shape aLocalShape = shellClosed.Reversed();
        solidMaker.Add(solid, TopoDS::Shell(aLocalShape));
    }

    solid.Closed(Standard_True);
    BRepLib::EncodeRegularity(solid);

    std::string loftName = _wing.GetUID();
    std::string loftShortName = _wing.GetShortShapeName();
    PNamedShape loft(new CNamedShape(solid, loftName.c_str(), loftShortName.c_str()));
    SetFaceTraits(_wing.GetUID(), loft, hasBluntTE);

    return loft;
}
#else
PNamedShape CTiglWingBuilder::BuildShape()
{
    const CCPACSWingSegments& segments = _wing.GetSegments();
    const CCPACSWingProfile& innerProfile = segments.GetSegment(1).GetInnerConnection().GetProfile();

    // we assume, that all profiles of one wing are either blunt or not
    // this is checked during cpacs loading of each wing segment
    bool hasBluntTE = innerProfile.HasBluntTE();

    CTiglMakeLoft lofter;
    lofter.setMakeSolid(true);

    for (int i=1; i <= segments.GetSegmentCount(); i++) {
        const TopoDS_Shape& startWire = segments.GetSegment(i).GetInnerWire();
        lofter.addProfiles(startWire);
    }

    TopoDS_Wire endWire =  segments.GetSegment(segments.GetSegmentCount()).GetOuterWire();
    lofter.addProfiles(endWire);

    // add guide curves
    lofter.addGuides(_wing.GetGuideCurveWires());

    TopoDS_Shape loftShape = lofter.Shape();
    std::string loftName = _wing.GetUID();
    std::string loftShortName = _wing.GetShortShapeName();
    PNamedShape loft(new CNamedShape(loftShape, loftName.c_str(), loftShortName.c_str()));
    SetFaceTraits(_wing.GetUID(), loft, hasBluntTE);
    return loft;
}
#endif

CTiglWingBuilder::operator PNamedShape()
{
    return BuildShape();
}
// Set the name of each wing face
void CTiglWingBuilder::SetFaceTraits (const std::string& wingUID, PNamedShape loft, bool hasBluntTE)
{
    unsigned int nSegments = _wing.GetSegmentCount();

    // designated names of the faces
    std::vector<std::string> names(3);
    names[0]="Bottom";
    names[1]="Top";
    names[2]="TrailingEdge";
    std::vector<std::string> endnames(2);
    endnames[0]="Inside";
    endnames[1]="Outside";

    unsigned int nFaces = GetNumberOfFaces(loft->Shape());

    for (unsigned int i = 0; i < nFaces; i++) {
        loft->FaceTraits(i).SetComponentUID(wingUID);
    }

    // check if number of faces without inside and outside surface (nFaces-2)
    // is a multiple of 2 (without Trailing Edges) or 3 (with Trailing Edges)
    if (!((nFaces-2)/nSegments == 2 || (nFaces-2)/nSegments == 3) || nFaces < 4) {
        LOG(ERROR) << "CCPACSWingBuilder: Unable to determine wing face names from wing loft.";
        return;
    }

#ifndef NO_EXPLICIT_TE_MODELING

    unsigned int nTEFaces = hasBluntTE ? nSegments : 0;
    unsigned int nAeroFaces = nFaces - nTEFaces - 2;

    // assign "Top" and "Bottom" to face traits
    for (unsigned int i = 0; i < nAeroFaces; i++) {
        CFaceTraits traits = loft->GetFaceTraits(i);
        traits.SetName(names[i%2].c_str());
        loft->SetFaceTraits(i, traits);
    }

    // assign TE to face traits
    for (unsigned int i = nAeroFaces; i < nAeroFaces + nTEFaces; ++i) {
        CFaceTraits traits = loft->GetFaceTraits(i);
        traits.SetName(names[2].c_str());
        loft->SetFaceTraits(i, traits);
    }
#else
    // remove trailing edge name if there is no trailing edge
    if (!hasBluntTE) {
        names.pop_back();
    }
    // assign "Top" and "Bottom" to face traits
    for (unsigned int i = 0; i < nFaces-2; i++) {
        CFaceTraits traits = loft->GetFaceTraits(i);
        traits.SetName(names[i%names.size()].c_str());
        loft->SetFaceTraits(i, traits);
    }
#endif

    // assign "Inside" and "Outside" to face traits
    for (unsigned int i = nFaces-2; i < nFaces; i++) {
        CFaceTraits traits = loft->GetFaceTraits(i);
        traits.SetName(endnames[i-nFaces+2].c_str());
        loft->SetFaceTraits(i, traits);
    }
}

// creates the inside and outside cap of the wing
Standard_Boolean CreateSideCap(const TopoDS_Wire& W,
                               const Standard_Real presPln,
                               TopoDS_Face& theFace)
{
    Standard_Boolean isDegen = Standard_True;
    TopoDS_Iterator iter(W);
    for (; iter.More(); iter.Next())
    {
        const TopoDS_Edge& anEdge = TopoDS::Edge(iter.Value());
        if (!BRep_Tool::Degenerated(anEdge))
            isDegen = Standard_False;
    }
    if (isDegen)
        return Standard_True;

    Standard_Boolean Ok = Standard_False;
    if (!W.IsNull()) {
        BRepBuilderAPI_FindPlane Searcher( W, presPln );
        if (Searcher.Found()) {
            theFace = BRepBuilderAPI_MakeFace(Searcher.Plane(), W);
            Ok = Standard_True;
        }
        else {
            // try to find another surface
            BRepBuilderAPI_MakeFace MF( W );
            if (MF.IsDone())
            {
                theFace = MF.Face();
                Ok = Standard_True;
            }
        }
    }

    return Ok;
}

} //namespace tigl
<commit_msg>added check for shell in CTiglWingBuilder<commit_after>/*
* Copyright (C) 2015 German Aerospace Center (DLR/SC)
*
* Created: 2015-10-20 Martin Siggel <Martin.Siggel@dlr.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "CTiglWingBuilder.h"

#include "CTiglMakeLoft.h"
#include "CCPACSWingSegment.h"
#include "CTiglLogging.h"
#include "CNamedShape.h"
#include "tiglcommonfunctions.h"

#include <TopExp.hxx>
#include <TopoDS_Iterator.hxx>
#include <TopTools_IndexedMapOfShape.hxx>
#include <BRepBuilderAPI_MakeWire.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include <BRepOffsetAPI_ThruSections.hxx>
#include <BRepBuilderAPI_Sewing.hxx>
#include <BRepClass3d_SolidClassifier.hxx>
#include <BRep_Builder.hxx>
#include <BRepBuilderAPI_FindPlane.hxx>
#include <BRepBuilderAPI_MakeFace.hxx>
#include <BRepLib.hxx>
#include <Geom_Plane.hxx>

#include <cassert>

#define NO_EXPLICIT_TE_MODELING

namespace tigl
{

Standard_Boolean CreateSideCap(const TopoDS_Wire& W,
                               const Standard_Real presPln,
                               TopoDS_Face& theFace);

CTiglWingBuilder::CTiglWingBuilder(const CCPACSWing& wing)
    : _wing(wing)
{
}

#ifndef NO_EXPLICIT_TE_MODELING
PNamedShape CTiglWingBuilder::BuildShape()
{
    const CCPACSWingSegments& segments = _wing.m_segments;

    // check whether we have a blunt TE or not
    const CCPACSWingProfile& innerProfile = segments.GetSegment(1).GetInnerConnection().GetProfile();

    TopoDS_Compound guideCurves = _wing.GetGuideCurveWires();

    std::vector<TopoDS_Wire> guides;
    for (TopoDS_Iterator anIter(guideCurves); anIter.More(); anIter.Next()) {
        TopoDS_Wire aSh = TopoDS::Wire(anIter.Value());
        guides.push_back(aSh);
    }

    // we assume, that all profiles of one wing are either blunt or not
    // this is checked during cpacs loading of each wing segment
    bool hasBluntTE = innerProfile.HasBluntTE();
    bool hasGuideCurves = guides.size() > 0;

    CTiglMakeLoft lofter;
    lofter.setMakeSolid(false);

    std::vector<gp_Pnt> upperTEPoints, lowerTEPoints;
    for (int i=1; i <= segments.GetSegmentCount(); i++) {
        const TopoDS_Wire& startWire = segments.GetSegment(i).GetInnerWire();

        // extract trailing edge, in case of a blunt TE
        // we assume, that the TE is the last edge of the wire
        if (hasBluntTE) {
            BRepBuilderAPI_MakeWire wireMaker;
            TopTools_IndexedMapOfShape edgeMap;
            TopExp::MapShapes(startWire, TopAbs_EDGE, edgeMap);
            for (int i = 1; i <= edgeMap.Extent(); ++i) {
                const TopoDS_Edge& e = TopoDS::Edge(edgeMap(i));
                if (i < edgeMap.Extent()) {
                    wireMaker.Add(e);
                }
            }
            TopoDS_Wire aeroProfile = wireMaker.Wire();
            lofter.addProfiles(aeroProfile);
            upperTEPoints.push_back(GetLastPoint(aeroProfile));
            lowerTEPoints.push_back(GetFirstPoint(aeroProfile));
        }
        else {
            lofter.addProfiles(startWire);
        }
    }

    TopoDS_Wire endWire =  segments.GetSegment(segments.GetSegmentCount()).GetOuterWire();
    if (hasBluntTE) {
        // extract the wire without the trailing edge

        BRepBuilderAPI_MakeWire wireMaker;
        TopTools_IndexedMapOfShape edgeMap;
        TopExp::MapShapes(endWire, TopAbs_EDGE, edgeMap);
        for (int i = 1; i <= edgeMap.Extent(); ++i) {
            const TopoDS_Edge& e = TopoDS::Edge(edgeMap(i));
            if (i < edgeMap.Extent()) {
                wireMaker.Add(e);
            }
        }
        TopoDS_Wire aeroProfile = wireMaker.Wire();
        lofter.addProfiles(aeroProfile);
        upperTEPoints.push_back(GetLastPoint(aeroProfile));
        lowerTEPoints.push_back(GetFirstPoint(aeroProfile));
    }
    else {
        lofter.addProfiles(endWire);
    }

    // add guide curves
    lofter.addGuides(guideCurves);

    TopoDS_Shape aeroShape = lofter.Shape();
    BRepBuilderAPI_Sewing sewingAlgo;
    sewingAlgo.Add(aeroShape);

    // If blunt trailing edge is available, model it explicitly
    if (hasBluntTE) {

        TopoDS_Wire upperTE, lowerTE;
        if (hasGuideCurves) {
            // The guide curves are sorted in the order of the relative starting coordinate
            // at the innermost section
            upperTE = guides.back();
            lowerTE = guides.front();
        }
        else {
            // model the edges by taking the first and last profile points

            assert(lowerTEPoints.size() == upperTEPoints.size());
            assert(lowerTEPoints.size() > 1);

            // build upper edge
            BRepBuilderAPI_MakeWire upperWireMaker, lowerWireMaker;
            for (unsigned int i = 0; i < upperTEPoints.size() - 1; ++i) {
                upperWireMaker.Add(BRepBuilderAPI_MakeEdge(upperTEPoints.at(i), upperTEPoints.at(i+1)));
                lowerWireMaker.Add(BRepBuilderAPI_MakeEdge(lowerTEPoints.at(i), lowerTEPoints.at(i+1)));
            }
            upperTE = upperWireMaker.Wire();
            lowerTE = lowerWireMaker.Wire();
        }

        // the TE is build using a ruled loft
        BRepOffsetAPI_ThruSections trailingEdgeBuilder(Standard_False, Standard_True);
        trailingEdgeBuilder.AddWire(upperTE);
        trailingEdgeBuilder.AddWire(lowerTE);
        trailingEdgeBuilder.Build();
        TopoDS_Shape trailingEdge = trailingEdgeBuilder.Shape();
        sewingAlgo.Add(trailingEdge);

    } // hasBluntTE

    // get the profiles
    TopoDS_Wire innerWire = segments.GetSegment(1).GetInnerWire();
    TopoDS_Wire outerWire = segments.GetSegment(segments.GetSegmentCount()).GetOuterWire();

    TopoDS_Face innerFace, outerFace;
    CreateSideCap(innerWire, 1e-6, innerFace);
    CreateSideCap(outerWire, 1e-6, outerFace);

    sewingAlgo.Add(innerFace);
    sewingAlgo.Add(outerFace);

    sewingAlgo.Perform();
    TopoDS_Shape shellClosed  = sewingAlgo.SewedShape();
    if (shellClosed.ShapeType() != TopAbs_SHELL)
        throw CTiglError("Expected sewing algo to construct a shell when building wing loft");
    shellClosed.Closed(Standard_True);

    // make solid from shell
    TopoDS_Solid solid;
    BRep_Builder solidMaker;
    solidMaker.MakeSolid(solid);
    solidMaker.Add(solid, shellClosed);

    // verify the orientation the solid
    BRepClass3d_SolidClassifier clas3d(solid);
    clas3d.PerformInfinitePoint(Precision::Confusion());
    if (clas3d.State() == TopAbs_IN) {
     solidMaker.MakeSolid(solid);
        TopoDS_Shape aLocalShape = shellClosed.Reversed();
        solidMaker.Add(solid, TopoDS::Shell(aLocalShape));
    }

    solid.Closed(Standard_True);
    BRepLib::EncodeRegularity(solid);

    std::string loftName = _wing.GetUID();
    std::string loftShortName = _wing.GetShortShapeName();
    PNamedShape loft(new CNamedShape(solid, loftName.c_str(), loftShortName.c_str()));
    SetFaceTraits(_wing.GetUID(), loft, hasBluntTE);

    return loft;
}
#else
PNamedShape CTiglWingBuilder::BuildShape()
{
    const CCPACSWingSegments& segments = _wing.GetSegments();
    const CCPACSWingProfile& innerProfile = segments.GetSegment(1).GetInnerConnection().GetProfile();

    // we assume, that all profiles of one wing are either blunt or not
    // this is checked during cpacs loading of each wing segment
    bool hasBluntTE = innerProfile.HasBluntTE();

    CTiglMakeLoft lofter;
    lofter.setMakeSolid(true);

    for (int i=1; i <= segments.GetSegmentCount(); i++) {
        const TopoDS_Shape& startWire = segments.GetSegment(i).GetInnerWire();
        lofter.addProfiles(startWire);
    }

    TopoDS_Wire endWire =  segments.GetSegment(segments.GetSegmentCount()).GetOuterWire();
    lofter.addProfiles(endWire);

    // add guide curves
    lofter.addGuides(_wing.GetGuideCurveWires());

    TopoDS_Shape loftShape = lofter.Shape();
    std::string loftName = _wing.GetUID();
    std::string loftShortName = _wing.GetShortShapeName();
    PNamedShape loft(new CNamedShape(loftShape, loftName.c_str(), loftShortName.c_str()));
    SetFaceTraits(_wing.GetUID(), loft, hasBluntTE);
    return loft;
}
#endif

CTiglWingBuilder::operator PNamedShape()
{
    return BuildShape();
}
// Set the name of each wing face
void CTiglWingBuilder::SetFaceTraits (const std::string& wingUID, PNamedShape loft, bool hasBluntTE)
{
    unsigned int nSegments = _wing.GetSegmentCount();

    // designated names of the faces
    std::vector<std::string> names(3);
    names[0]="Bottom";
    names[1]="Top";
    names[2]="TrailingEdge";
    std::vector<std::string> endnames(2);
    endnames[0]="Inside";
    endnames[1]="Outside";

    unsigned int nFaces = GetNumberOfFaces(loft->Shape());

    for (unsigned int i = 0; i < nFaces; i++) {
        loft->FaceTraits(i).SetComponentUID(wingUID);
    }

    // check if number of faces without inside and outside surface (nFaces-2)
    // is a multiple of 2 (without Trailing Edges) or 3 (with Trailing Edges)
    if (!((nFaces-2)/nSegments == 2 || (nFaces-2)/nSegments == 3) || nFaces < 4) {
        LOG(ERROR) << "CCPACSWingBuilder: Unable to determine wing face names from wing loft.";
        return;
    }

#ifndef NO_EXPLICIT_TE_MODELING

    unsigned int nTEFaces = hasBluntTE ? nSegments : 0;
    unsigned int nAeroFaces = nFaces - nTEFaces - 2;

    // assign "Top" and "Bottom" to face traits
    for (unsigned int i = 0; i < nAeroFaces; i++) {
        CFaceTraits traits = loft->GetFaceTraits(i);
        traits.SetName(names[i%2].c_str());
        loft->SetFaceTraits(i, traits);
    }

    // assign TE to face traits
    for (unsigned int i = nAeroFaces; i < nAeroFaces + nTEFaces; ++i) {
        CFaceTraits traits = loft->GetFaceTraits(i);
        traits.SetName(names[2].c_str());
        loft->SetFaceTraits(i, traits);
    }
#else
    // remove trailing edge name if there is no trailing edge
    if (!hasBluntTE) {
        names.pop_back();
    }
    // assign "Top" and "Bottom" to face traits
    for (unsigned int i = 0; i < nFaces-2; i++) {
        CFaceTraits traits = loft->GetFaceTraits(i);
        traits.SetName(names[i%names.size()].c_str());
        loft->SetFaceTraits(i, traits);
    }
#endif

    // assign "Inside" and "Outside" to face traits
    for (unsigned int i = nFaces-2; i < nFaces; i++) {
        CFaceTraits traits = loft->GetFaceTraits(i);
        traits.SetName(endnames[i-nFaces+2].c_str());
        loft->SetFaceTraits(i, traits);
    }
}

// creates the inside and outside cap of the wing
Standard_Boolean CreateSideCap(const TopoDS_Wire& W,
                               const Standard_Real presPln,
                               TopoDS_Face& theFace)
{
    Standard_Boolean isDegen = Standard_True;
    TopoDS_Iterator iter(W);
    for (; iter.More(); iter.Next())
    {
        const TopoDS_Edge& anEdge = TopoDS::Edge(iter.Value());
        if (!BRep_Tool::Degenerated(anEdge))
            isDegen = Standard_False;
    }
    if (isDegen)
        return Standard_True;

    Standard_Boolean Ok = Standard_False;
    if (!W.IsNull()) {
        BRepBuilderAPI_FindPlane Searcher( W, presPln );
        if (Searcher.Found()) {
            theFace = BRepBuilderAPI_MakeFace(Searcher.Plane(), W);
            Ok = Standard_True;
        }
        else {
            // try to find another surface
            BRepBuilderAPI_MakeFace MF( W );
            if (MF.IsDone())
            {
                theFace = MF.Face();
                Ok = Standard_True;
            }
        }
    }

    return Ok;
}

} //namespace tigl
<|endoftext|>


<commit_before>#include <memory>
#include <string>
#include <vector>
#include <unordered_map>
#include <utility>
#include <iomanip>
#include <CoreServices/CoreServices.h>

#include "platform.h"
#include "worker_thread.h"
#include "../log.h"
#include "../message.h"

using std::vector;
using std::string;
using std::endl;
using std::unique_ptr;
using std::move;
using std::pair;
using std::make_pair;
using std::unordered_map;
using std::hex;

static const CFAbsoluteTime LATENCY = 0.001;

static const FSEventStreamEventFlags CREATE_FLAGS = kFSEventStreamEventFlagItemCreated;

static const FSEventStreamEventFlags DELETED_FLAGS = kFSEventStreamEventFlagItemRemoved;

static const FSEventStreamEventFlags MODIFY_FLAGS = kFSEventStreamEventFlagItemInodeMetaMod |
  kFSEventStreamEventFlagItemFinderInfoMod |
  kFSEventStreamEventFlagItemChangeOwner |
  kFSEventStreamEventFlagItemXattrMod |
  kFSEventStreamEventFlagItemModified;

static const FSEventStreamEventFlags RENAME_FLAGS = kFSEventStreamEventFlagItemRenamed;

static const FSEventStreamEventFlags IS_FILE = kFSEventStreamEventFlagItemIsFile;

static const FSEventStreamEventFlags IS_DIRECTORY = kFSEventStreamEventFlagItemIsDir;

static void command_perform_helper(void *info);

static void event_stream_helper(
  ConstFSEventStreamRef event_stream,
  void *info,
  size_t num_events,
  void *event_paths,
  const FSEventStreamEventFlags *event_flags,
  const FSEventStreamEventId *event_ids);

struct Subscription {
  WorkerPlatform *platform;
  ChannelID channel;
  FSEventStreamRef event_stream;

  Subscription(WorkerPlatform *platform, ChannelID channel) : platform{platform}, channel{channel}, event_stream{NULL}
  {
    //
  }
};

class MacOSWorkerPlatform : public WorkerPlatform {
public:
  MacOSWorkerPlatform(WorkerThread *thread) :
    WorkerPlatform(thread),
    run_loop{nullptr},
    command_source{nullptr}
  {
    //
  };

  ~MacOSWorkerPlatform() override
  {
    if (command_source) CFRelease(command_source);
    if (run_loop) CFRelease(run_loop);
  }

  void wake() override
  {
    CFRunLoopSourceSignal(command_source);
    CFRunLoopWakeUp(run_loop);
  }

  void listen() override
  {
    run_loop = CFRunLoopGetCurrent();
    CFRetain(run_loop);

    CFRunLoopSourceContext command_context = {
      0, // version
      this, // info
      NULL, // retain
      NULL, // release
      NULL, // copyDescription
      NULL, // equal
      NULL, // hash
      NULL, // schedule
      NULL, // cancel
      command_perform_helper // perform
    };
    command_source = CFRunLoopSourceCreate(kCFAllocatorDefault, 1, &command_context);
    CFRunLoopAddSource(run_loop, command_source, kCFRunLoopDefaultMode);

    CFRunLoopRun();
    LOGGER << "Run loop ended unexpectedly." << endl;
  }

  void handle_add_command(const ChannelID channel, const string &root_path) override
  {
    LOGGER << "Adding watcher for path " << root_path << " at channel " << channel << "." << endl;

    Subscription *subscription = new Subscription(this, channel);

    FSEventStreamContext stream_context = {
      0, // version
      subscription, // info
      NULL, // retain
      NULL, // release
      NULL // copyDescription
    };

    CFStringRef watch_root = CFStringCreateWithBytes(
      kCFAllocatorDefault,
      reinterpret_cast<const UInt8*>(root_path.c_str()),
      root_path.size(),
      kCFStringEncodingUTF8,
      false
    );
    if (watch_root == NULL) {
      // TODO report error back in Ack
      return;
    }

    CFArrayRef watch_roots = CFArrayCreate(
      kCFAllocatorDefault,
      reinterpret_cast<const void**>(&watch_root),
      1,
      NULL
    );
    if (watch_roots == NULL) {
      // TODO report error back in Ack
      CFRelease(watch_root);
      return;
    }

    FSEventStreamRef event_stream = FSEventStreamCreate(
      kCFAllocatorDefault,
      event_stream_helper,
      &stream_context,
      watch_roots,
      kFSEventStreamEventIdSinceNow,
      LATENCY,
      kFSEventStreamCreateFlagNoDefer | kFSEventStreamCreateFlagFileEvents
    );
    subscription->event_stream = event_stream;

    subscriptions.insert(make_pair(channel, subscription));

    FSEventStreamScheduleWithRunLoop(event_stream, run_loop, kCFRunLoopDefaultMode);
    if (!FSEventStreamStart(event_stream)) {
      // TODO Report error back in Ack
    }

    CFRelease(watch_roots);
    CFRelease(watch_root);
  }

  void handle_remove_command(const ChannelID channel) override
  {
    LOGGER << "Removing watcher for channel " << channel << "." << endl;

    auto maybe_subscription = subscriptions.find(channel);
    if (maybe_subscription == subscriptions.end()) {
      LOGGER << "No subscription for channel " << channel << "." << endl;
      return;
    }

    Subscription *subscription = maybe_subscription->second;
    subscriptions.erase(maybe_subscription);

    FSEventStreamStop(subscription->event_stream);
    FSEventStreamInvalidate(subscription->event_stream);
    FSEventStreamRelease(subscription->event_stream);

    delete subscription;
  }

  void handle_fs_event(
    ChannelID channel,
    ConstFSEventStreamRef event_stream,
    size_t num_events,
    void *event_paths,
    const FSEventStreamEventFlags *event_flags,
    const FSEventStreamEventId *event_ids)
  {
    char **paths = reinterpret_cast<char**>(event_paths);
    vector<Message> messages;
    messages.reserve(num_events);

    for (size_t i = 0; i < num_events; i++) {
      bool created = (event_flags[i] & CREATE_FLAGS) != 0;
      bool deleted = (event_flags[i] & DELETED_FLAGS) != 0;
      bool modified = (event_flags[i] & MODIFY_FLAGS) != 0;
      bool renamed = (event_flags[i] & RENAME_FLAGS) != 0;

      bool is_file = (event_flags[i] & IS_FILE) != 0;
      bool is_directory = (event_flags[i] & IS_DIRECTORY) != 0;

      LOGGER << "Received event: "
        << (is_directory ? "directory" : "file")
        << " at [" << paths[i] << "]"
        << " created=" << created
        << " deleted=" << deleted
        << " modified=" << modified
        << " renamed=" << renamed
        << " flags=" << hex << event_flags[i]
        << endl;

      FileSystemAction action;
      if (created) {
        action = ACTION_CREATED;
      } else if (deleted) {
        action = ACTION_DELETED;
      } else if (modified) {
        action = ACTION_MODIFIED;
      } else if (renamed) {
        action = ACTION_RENAMED;
      } else {
        LOGGER << "Unknown filesystem event action from flags " << hex << event_flags[i] << "." << endl;
        continue;
      }

      EntryKind kind;
      if (is_file) {
        kind = KIND_FILE;
      } else if (is_directory) {
        kind = KIND_DIRECTORY;
      } else {
        LOGGER << "Unknown filesystem event entry kind from flags" << hex << event_flags[i] << "." << endl;
        continue;
      }

      string old_path(paths[i]);
      string new_path;

      FileSystemPayload payload(channel, action, kind, move(old_path), move(new_path));
      Message event_message(move(payload));

      LOGGER << "Emitting filesystem message " << event_message << endl;

      messages.push_back(move(event_message));
    }

    emit_all(messages.begin(), messages.end());
  }

private:
  CFRunLoopRef run_loop;
  CFRunLoopSourceRef command_source;

  unordered_map<ChannelID, Subscription*> subscriptions;
};

static void command_perform_helper(void *info)
{
  MacOSWorkerPlatform *platform = reinterpret_cast<MacOSWorkerPlatform*>(info);
  platform->handle_commands();
}

static void event_stream_helper(
  ConstFSEventStreamRef event_stream,
  void *info,
  size_t num_events,
  void *event_paths,
  const FSEventStreamEventFlags *event_flags,
  const FSEventStreamEventId *event_ids)
{
  Subscription *sub = reinterpret_cast<Subscription*>(info);
  MacOSWorkerPlatform *platform = static_cast<MacOSWorkerPlatform*>(sub->platform);

  platform->handle_fs_event(sub->channel, event_stream, num_events, event_paths, event_flags, event_ids);
}

unique_ptr<WorkerPlatform> WorkerPlatform::for_worker(WorkerThread *thread)
{
  return unique_ptr<WorkerPlatform>(new MacOSWorkerPlatform(thread));
}
<commit_msg>Use a cache of recently seen paths to disambiguate coalesced Mac events<commit_after>#include <memory>
#include <string>
#include <vector>
#include <unordered_set>
#include <unordered_map>
#include <map>
#include <chrono>
#include <utility>
#include <iomanip>
#include <CoreServices/CoreServices.h>
#include <sys/stat.h>
#include <errno.h>

#include "platform.h"
#include "worker_thread.h"
#include "../log.h"
#include "../message.h"

using std::vector;
using std::string;
using std::endl;
using std::unique_ptr;
using std::move;
using std::pair;
using std::make_pair;
using std::unordered_set;
using std::unordered_map;
using std::multimap;
using std::hex;
using std::chrono::steady_clock;
using std::chrono::time_point;
using std::chrono::duration;
using std::chrono::seconds;

static const CFAbsoluteTime LATENCY = 0;

static const FSEventStreamEventFlags CREATE_FLAGS = kFSEventStreamEventFlagItemCreated;

static const FSEventStreamEventFlags DELETED_FLAGS = kFSEventStreamEventFlagItemRemoved;

static const FSEventStreamEventFlags MODIFY_FLAGS = kFSEventStreamEventFlagItemInodeMetaMod |
  kFSEventStreamEventFlagItemFinderInfoMod |
  kFSEventStreamEventFlagItemChangeOwner |
  kFSEventStreamEventFlagItemXattrMod |
  kFSEventStreamEventFlagItemModified;

static const FSEventStreamEventFlags RENAME_FLAGS = kFSEventStreamEventFlagItemRenamed;

static const FSEventStreamEventFlags IS_FILE = kFSEventStreamEventFlagItemIsFile;

static const FSEventStreamEventFlags IS_DIRECTORY = kFSEventStreamEventFlagItemIsDir;

static void command_perform_helper(void *info);

static void event_stream_helper(
  ConstFSEventStreamRef event_stream,
  void *info,
  size_t num_events,
  void *event_paths,
  const FSEventStreamEventFlags *event_flags,
  const FSEventStreamEventId *event_ids);

struct Subscription {
  WorkerPlatform *platform;
  ChannelID channel;
  FSEventStreamRef event_stream;

  Subscription(WorkerPlatform *platform, ChannelID channel) : platform{platform}, channel{channel}, event_stream{NULL}
  {
    //
  }
};

class RecentFileCache {
public:
  void does_exist(const string &path)
  {
    bool inserted = seen_paths.insert(path).second;
    if (inserted) {
      time_point<steady_clock> ts = steady_clock::now();
      by_timestamp.insert({ts, path});
    }
  }

  void does_not_exist(const string &path)
  {
    seen_paths.erase(path);
  }

  bool has_been_seen(const string &path)
  {
    return seen_paths.find(path) != seen_paths.end();
  }

  void purge()
  {
    time_point<steady_clock> oldest = steady_clock::now() - seconds(5);

    auto to_keep = by_timestamp.upper_bound(oldest);
    for (auto it = by_timestamp.begin(); it != to_keep && it != by_timestamp.end(); ++it) {
      seen_paths.erase(it->second);
    }

    if (to_keep != by_timestamp.begin()) {
      by_timestamp.erase(by_timestamp.begin(), to_keep);
    }
  }

private:
  unordered_set<string> seen_paths;
  multimap<time_point<steady_clock>, string> by_timestamp;
};

class MacOSWorkerPlatform : public WorkerPlatform {
public:
  MacOSWorkerPlatform(WorkerThread *thread) :
    WorkerPlatform(thread),
    run_loop{nullptr},
    command_source{nullptr}
  {
    //
  };

  ~MacOSWorkerPlatform() override
  {
    if (command_source) CFRelease(command_source);
    if (run_loop) CFRelease(run_loop);
  }

  void wake() override
  {
    CFRunLoopSourceSignal(command_source);
    CFRunLoopWakeUp(run_loop);
  }

  void listen() override
  {
    run_loop = CFRunLoopGetCurrent();
    CFRetain(run_loop);

    CFRunLoopSourceContext command_context = {
      0, // version
      this, // info
      NULL, // retain
      NULL, // release
      NULL, // copyDescription
      NULL, // equal
      NULL, // hash
      NULL, // schedule
      NULL, // cancel
      command_perform_helper // perform
    };
    command_source = CFRunLoopSourceCreate(kCFAllocatorDefault, 1, &command_context);
    CFRunLoopAddSource(run_loop, command_source, kCFRunLoopDefaultMode);

    CFRunLoopRun();
    LOGGER << "Run loop ended unexpectedly." << endl;
  }

  void handle_add_command(const ChannelID channel, const string &root_path) override
  {
    LOGGER << "Adding watcher for path " << root_path << " at channel " << channel << "." << endl;

    Subscription *subscription = new Subscription(this, channel);

    FSEventStreamContext stream_context = {
      0, // version
      subscription, // info
      NULL, // retain
      NULL, // release
      NULL // copyDescription
    };

    CFStringRef watch_root = CFStringCreateWithBytes(
      kCFAllocatorDefault,
      reinterpret_cast<const UInt8*>(root_path.c_str()),
      root_path.size(),
      kCFStringEncodingUTF8,
      false
    );
    if (watch_root == NULL) {
      // TODO report error back in Ack
      return;
    }

    CFArrayRef watch_roots = CFArrayCreate(
      kCFAllocatorDefault,
      reinterpret_cast<const void**>(&watch_root),
      1,
      NULL
    );
    if (watch_roots == NULL) {
      // TODO report error back in Ack
      CFRelease(watch_root);
      return;
    }

    FSEventStreamRef event_stream = FSEventStreamCreate(
      kCFAllocatorDefault,
      event_stream_helper,
      &stream_context,
      watch_roots,
      kFSEventStreamEventIdSinceNow,
      LATENCY,
      kFSEventStreamCreateFlagNoDefer | kFSEventStreamCreateFlagFileEvents
    );
    subscription->event_stream = event_stream;

    subscriptions.insert(make_pair(channel, subscription));

    FSEventStreamScheduleWithRunLoop(event_stream, run_loop, kCFRunLoopDefaultMode);
    if (!FSEventStreamStart(event_stream)) {
      // TODO Report error back in Ack
    }

    CFRelease(watch_roots);
    CFRelease(watch_root);
  }

  void handle_remove_command(const ChannelID channel) override
  {
    LOGGER << "Removing watcher for channel " << channel << "." << endl;

    auto maybe_subscription = subscriptions.find(channel);
    if (maybe_subscription == subscriptions.end()) {
      LOGGER << "No subscription for channel " << channel << "." << endl;
      return;
    }

    Subscription *subscription = maybe_subscription->second;
    subscriptions.erase(maybe_subscription);

    FSEventStreamStop(subscription->event_stream);
    FSEventStreamInvalidate(subscription->event_stream);
    FSEventStreamRelease(subscription->event_stream);

    delete subscription;
  }

  void handle_fs_event(
    ChannelID channel,
    ConstFSEventStreamRef event_stream,
    size_t num_events,
    void *event_paths,
    const FSEventStreamEventFlags *event_flags,
    const FSEventStreamEventId *event_ids)
  {
    char **paths = reinterpret_cast<char**>(event_paths);
    vector<Message> messages;
    messages.reserve(num_events);

    LOGGER << "Filesystem event batch of size " << num_events << " received." << endl;

    for (size_t i = 0; i < num_events; i++) {
      bool created = (event_flags[i] & CREATE_FLAGS) != 0;
      bool deleted = (event_flags[i] & DELETED_FLAGS) != 0;
      bool modified = (event_flags[i] & MODIFY_FLAGS) != 0;
      bool renamed = (event_flags[i] & RENAME_FLAGS) != 0;

      bool is_file = (event_flags[i] & IS_FILE) != 0;
      bool is_directory = (event_flags[i] & IS_DIRECTORY) != 0;

      string event_path(paths[i]);
      bool stat_performed = false;
      int stat_errno = 0;

      struct stat path_stat;

      LOGGER << "Received event: "
        << event_ids[i] << " => "
        << (is_directory ? "directory" : "file")
        << " at [" << event_path << "]"
        << " created=" << created
        << " deleted=" << deleted
        << " modified=" << modified
        << " renamed=" << renamed
        << " flags=" << hex << event_flags[i]
        << endl;

      EntryKind kind;
      if (is_file && !is_directory) {
        kind = KIND_FILE;
      } else if (is_directory && !is_file) {
        kind = KIND_DIRECTORY;
      } else {
        // FIXME will file and directory events ever be coalesced?
        // FIXME handle symlinks, named pipes, etc
        LOGGER << "Unknown or ambiguous filesystem event entry kind from flags " << hex << event_flags[i] << "." << endl;

        if (lstat(event_path.c_str(), &path_stat) != 0) {
          stat_errno = errno;
        }
        stat_performed = true;
        // FIXME handle stat errors

        if ((path_stat.st_mode & S_IFDIR) != 0) {
          kind = KIND_DIRECTORY;
        } else if ((path_stat.st_mode & S_IFREG) != 0) {
          kind = KIND_FILE;
        } else {
          LOGGER << "Unhandled stat() flags " << hex << path_stat.st_mode << "." << endl;
          continue;
        }
      }

      // Uncoalesed, single-event flags.

      if (created && !(deleted || modified || renamed)) {
        cache.does_exist(event_path);
        enqueue_creation(messages, channel, kind, event_path);
        continue;
      }

      if (deleted && !(created || modified || renamed)) {
        cache.does_not_exist(event_path);
        enqueue_deletion(messages, channel, kind, event_path);
        continue;
      }

      if (modified && !(created || deleted || renamed)) {
        cache.does_exist(event_path);
        enqueue_modification(messages, channel, kind, event_path);
        continue;
      }

      // Call lstat() to note the final state of the entry.
      if (!stat_performed) {
        if (lstat(event_path.c_str(), &path_stat) != 0) {
          stat_errno = errno;
        }
        stat_performed = true;
      }

      if (stat_errno == ENOENT) {
        // The entry no longer exists. Emit a creation event if it has never been seen before, then emit a deletion
        // event.

        if (!cache.has_been_seen(event_path)) {
          enqueue_creation(messages, channel, kind, event_path);
        }
        enqueue_deletion(messages, channel, kind, event_path);
        continue;
      }

      // The entry currently exists. Determine if a creation or a modification event is appropriate.

      bool seen_before = cache.has_been_seen(event_path);
      cache.does_exist(event_path);

      if (seen_before) {
        // This is *not* the first time an event at this path has been seen.
        if (deleted) {
          // Rapid creation and deletion. There may be a lost modification event just before deletion or just after
          // recreation.
          enqueue_deletion(messages, channel, kind, event_path);
          enqueue_creation(messages, channel, kind, event_path);
        } else {
          // Modification of an existing file.
          enqueue_modification(messages, channel, kind, event_path);
        }
      } else {
        // This *is* the first time an event has been seen at this path.
        if (deleted) {
          // The only way for the deletion flag to be set on a file we haven't seen before is for the file to
          // be rapidly created, deleted, and created again.
          enqueue_creation(messages, channel, kind, event_path);
          enqueue_deletion(messages, channel, kind, event_path);
          enqueue_creation(messages, channel, kind, event_path);
        } else {
          // Otherwise, it must have been created. This may conceal a separate modification event just after
          // the file's creation.
          enqueue_creation(messages, channel, kind, event_path);
        }
      }
    }

    emit_all(messages.begin(), messages.end());

    LOGGER << "Filesystem event batch of size " << num_events << " completed. "
      << messages.size() << " message(s) produced." << endl;

    cache.purge();
  }

private:
  void enqueue_creation(
    vector<Message> &messages,
    const ChannelID &channel,
    const EntryKind &kind,
    string created_path)
  {
    string empty_path;

    FileSystemPayload payload(channel, ACTION_CREATED, kind, move(created_path), move(empty_path));
    Message event_message(move(payload));

    LOGGER << "Emitting filesystem message " << event_message << endl;

    messages.push_back(move(event_message));
  }

  void enqueue_modification(
    vector<Message> &messages,
    const ChannelID &channel,
    const EntryKind &kind,
    string modified_path)
  {
    string empty_path;

    FileSystemPayload payload(channel, ACTION_MODIFIED, kind, move(modified_path), move(empty_path));
    Message event_message(move(payload));

    LOGGER << "Emitting filesystem message " << event_message << endl;

    messages.push_back(move(event_message));
  }

  void enqueue_deletion(
    vector<Message> &messages,
    const ChannelID &channel,
    const EntryKind &kind,
    string deleted_path)
  {
    string empty_path;

    FileSystemPayload payload(channel, ACTION_DELETED, kind, move(deleted_path), move(empty_path));
    Message event_message(move(payload));

    LOGGER << "Emitting filesystem message " << event_message << endl;

    messages.push_back(move(event_message));
  }

  void enqueue_rename(
    vector<Message> &messages,
    const ChannelID &channel,
    const EntryKind &kind,
    string old_path,
    string new_path)
  {
    FileSystemPayload payload(channel, ACTION_DELETED, kind, move(old_path), move(new_path));
    Message event_message(move(payload));

    LOGGER << "Emitting filesystem message " << event_message << endl;

    messages.push_back(move(event_message));
  }

  CFRunLoopRef run_loop;
  CFRunLoopSourceRef command_source;

  unordered_map<ChannelID, Subscription*> subscriptions;
  RecentFileCache cache;
};

static void command_perform_helper(void *info)
{
  MacOSWorkerPlatform *platform = reinterpret_cast<MacOSWorkerPlatform*>(info);
  platform->handle_commands();
}

static void event_stream_helper(
  ConstFSEventStreamRef event_stream,
  void *info,
  size_t num_events,
  void *event_paths,
  const FSEventStreamEventFlags *event_flags,
  const FSEventStreamEventId *event_ids)
{
  Subscription *sub = reinterpret_cast<Subscription*>(info);
  MacOSWorkerPlatform *platform = static_cast<MacOSWorkerPlatform*>(sub->platform);

  platform->handle_fs_event(sub->channel, event_stream, num_events, event_paths, event_flags, event_ids);
}

unique_ptr<WorkerPlatform> WorkerPlatform::for_worker(WorkerThread *thread)
{
  return unique_ptr<WorkerPlatform>(new MacOSWorkerPlatform(thread));
}
<|endoftext|>


<commit_before>/*=========================================================================
 *
 *  Copyright NumFOCUS
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *=========================================================================*/
#ifndef itkRelabelComponentImageFilter_hxx
#define itkRelabelComponentImageFilter_hxx

#include "itkRelabelComponentImageFilter.h"
#include "itkImageRegionIterator.h"
#include "itkNumericTraits.h"
#include "itkProgressReporter.h"
#include "itkProgressTransformer.h"
#include "itkImageScanlineIterator.h"
#include <map>
#include <utility>

namespace itk
{
template <typename TInputImage, typename TOutputImage>
void
RelabelComponentImageFilter<TInputImage, TOutputImage>::GenerateInputRequestedRegion()
{
  // call the superclass' implementation of this method
  Superclass::GenerateInputRequestedRegion();

  // We need all the input.
  InputImagePointer input = const_cast<InputImageType *>(this->GetInput());
  if (input)
  {
    input->SetRequestedRegion(input->GetLargestPossibleRegion());
  }
}

template <typename TInputImage, typename TOutputImage>
void
RelabelComponentImageFilter<TInputImage, TOutputImage>::ParallelComputeLabels(const RegionType & inputRegionForThread)
{
  RelabelComponentObjectType initialSize;
  initialSize.m_SizeInPixels = 0;

  // walk the input
  ImageScanlineConstIterator<InputImageType> it(this->GetInput(), inputRegionForThread);

  MapType localSizeMap;

  auto mapIt = localSizeMap.end();
  while (!it.IsAtEnd())
  {
    while (!it.IsAtEndOfLine())
    {
      // Get the input pixel value
      const auto inputValue = it.Get();

      // if the input pixel is not the background
      if (inputValue != NumericTraits<LabelType>::ZeroValue())
      {
        // label is not currently in the map
        mapIt = localSizeMap.insert(mapIt, { inputValue, initialSize });

        // label is already in the map, update the values
        ++(mapIt->second.m_SizeInPixels);
      }

      // increment the iterator
      ++it;
    }
    it.NextLine();
  }

  // Merge localStatistics and m_LabelStatistics concurrently safe in a
  // local copy, this thread may do multiple merges.
  while (true)
  {
    std::unique_lock<std::mutex> lock(m_Mutex);

    if (m_SizeMap.empty())
    {
      swap(m_SizeMap, localSizeMap);
      break;
    }
    else
    {
      // copy the output map to thread local storage
      MapType toMerge;
      swap(m_SizeMap, toMerge);

      // allow other threads to merge data
      lock.unlock();

      // Merge toMerge into localSizeMap, locally
      for (auto & sizePair : toMerge)
      {
        localSizeMap[sizePair.first] += sizePair.second;
      }
    }
  } // release lock
}

template <typename TInputImage, typename TOutputImage>
void
RelabelComponentImageFilter<TInputImage, TOutputImage>::GenerateData()
{
  using LabelComponentPairType = std::pair<LabelType, RelabelComponentObjectType>;

  // Get the input and the output
  const TInputImage * input = this->GetInput();
  TOutputImage *      output = this->GetOutput();

  // Calculate the size of pixel
  float physicalPixelSize = 1.0;
  for (unsigned int i = 0; i < TInputImage::ImageDimension; ++i)
  {
    physicalPixelSize *= input->GetSpacing()[i];
  }

  // Walk the entire input image and compute used labels and the number of each label.
  ProgressTransformer progressTransformer1(0.0f, 0.5f, this);
  this->GetMultiThreader()->template ParallelizeImageRegion<ImageDimension>(
    input->GetRequestedRegion(),
    [this](const RegionType & inputRegion) { this->ParallelComputeLabels(inputRegion); },
    progressTransformer1.GetProcessObject());

  // Construct an array of the label, component information pair to sort
  auto sizeVector = std::vector<LabelComponentPairType>(m_SizeMap.begin(), m_SizeMap.end());

  // free memory by swapping to a default constructed object.
  MapType().swap(m_SizeMap);

  // Sort the objects by size by default, unless m_SortByObjectSize
  // is set to false.
  if (m_SortByObjectSize)
  {
    std::sort(sizeVector.begin(),
              sizeVector.end(),
              [](const LabelComponentPairType & a, const LabelComponentPairType & b) -> bool {
                return a.second.m_SizeInPixels > b.second.m_SizeInPixels ||
                       (!(a.second.m_SizeInPixels < b.second.m_SizeInPixels) && a.first < b.first);
              });
  }

  // A map from the input pixel labels to the output labels
  using RelabelMapType = std::map<LabelType, OutputPixelType>;
  RelabelMapType relabelMap;

  // create a lookup table to map the input label to the output label.
  // cache the object sizes for later access by the user
  m_NumberOfObjects = sizeVector.size();
  m_OriginalNumberOfObjects = sizeVector.size();
  m_SizeOfObjectsInPixels.clear();
  m_SizeOfObjectsInPixels.resize(m_NumberOfObjects);
  SizeValueType   NumberOfObjectsRemoved = 0;
  OutputPixelType outputLabel = 0;
  for (const auto & sizeVectorPair : sizeVector)
  {
    // skip objects that are too small ( but don't increment the output label )
    if (m_MinimumObjectSize > 0 && sizeVectorPair.second.m_SizeInPixels < m_MinimumObjectSize)
    {
      // map small objects to the background
      ++NumberOfObjectsRemoved;
      relabelMap.insert({ sizeVectorPair.first, NumericTraits<OutputPixelType>::ZeroValue() });
    }
    else
    {
      if (outputLabel == NumericTraits<OutputPixelType>::max())
      {
        itkExceptionMacro("Output range exceeded!");
      }
      // map for input labels to output labels (Note we use i+1 in the
      // map since index 0 is the background)
      relabelMap.insert({ sizeVectorPair.first, outputLabel + 1 });

      // cache object sizes for later access by the user
      m_SizeOfObjectsInPixels[outputLabel] = sizeVectorPair.second.m_SizeInPixels;
      ++outputLabel;
    }
  }

  // update number of objects and resize vectors if we have removed small
  // objects
  m_NumberOfObjects -= NumberOfObjectsRemoved;
  if (NumberOfObjectsRemoved > 0)
  {
    m_SizeOfObjectsInPixels.resize(m_NumberOfObjects);
  }

  // compute the object sizes in physical space too
  m_SizeOfObjectsInPhysicalUnits.resize(m_NumberOfObjects);
  std::transform(m_SizeOfObjectsInPixels.begin(),
                 m_SizeOfObjectsInPixels.end(),
                 m_SizeOfObjectsInPhysicalUnits.begin(),
                 [physicalPixelSize](ObjectSizeType sizeInPixels) { return sizeInPixels * physicalPixelSize; });

  // After the objects stats are computed add in the background label so the relabelMap can be directly applied.
  relabelMap.insert({ NumericTraits<LabelType>::ZeroValue(), NumericTraits<OutputPixelType>::ZeroValue() });

  // Second pass: walk just the output requested region and relabel
  // the necessary pixels.
  //

  // Allocate the output
  this->AllocateOutputs();

  // In parallel apply the relabling map
  ProgressTransformer progressTransformer2(0.5f, 1.0f, this);
  this->GetMultiThreader()->template ParallelizeImageRegion<ImageDimension>(
    output->GetRequestedRegion(),
    [this, &relabelMap](const RegionType & outputRegionForThread) {
      ImageScanlineIterator<OutputImageType>     oit(this->GetOutput(), outputRegionForThread);
      ImageScanlineConstIterator<InputImageType> it(this->GetInput(), outputRegionForThread);

      auto mapIt = relabelMap.cbegin();

      while (!oit.IsAtEnd())
      {
        while (!oit.IsAtEndOfLine())
        {
          const auto && inputValue = it.Get();

          if (mapIt->first != inputValue)
          {
            mapIt = relabelMap.find(inputValue);
          }

          // no new labels should be encountered in the input
          assert(mapIt != relabelMap.cend());

          oit.Set(mapIt->second);

          ++oit;
          ++it;
        }
        oit.NextLine();
        it.NextLine();
      }
    },
    progressTransformer2.GetProcessObject());
}

template <typename TInputImage, typename TOutputImage>
void
RelabelComponentImageFilter<TInputImage, TOutputImage>::PrintSelf(std::ostream & os, Indent indent) const
{
  Superclass::PrintSelf(os, indent);

  os << indent << "NumberOfObjects: " << m_NumberOfObjects << std::endl;
  os << indent << "OriginalNumberOfObjects: " << m_OriginalNumberOfObjects << std::endl;
  os << indent << "NumberOfObjectsToPrint: " << m_NumberOfObjectsToPrint << std::endl;
  os << indent << "MinimumObjectSizes: " << m_MinimumObjectSize << std::endl;
  os << indent << "SortByObjectSize: " << m_SortByObjectSize << std::endl;

  typename ObjectSizeInPixelsContainerType::const_iterator it;
  ObjectSizeInPhysicalUnitsContainerType::const_iterator   fit;
  SizeValueType                                            i;

  // limit the number of objects to print
  SizeValueType numPrint = std::min(m_NumberOfObjectsToPrint, m_SizeOfObjectsInPixels.size());

  for (i = 0, it = m_SizeOfObjectsInPixels.begin(), fit = m_SizeOfObjectsInPhysicalUnits.begin(); i < numPrint;
       ++it, ++fit, ++i)
  {
    os << indent << "Object #" << i + 1 << ": " << *it << " pixels, " << *fit << " physical units" << std::endl;
  }
  if (numPrint < m_SizeOfObjectsInPixels.size())
  {
    os << indent << "..." << std::endl;
  }
}
} // end namespace itk

#endif
<commit_msg>COMP: Need to explicitly define std::min<> template values<commit_after>/*=========================================================================
 *
 *  Copyright NumFOCUS
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *=========================================================================*/
#ifndef itkRelabelComponentImageFilter_hxx
#define itkRelabelComponentImageFilter_hxx

#include "itkRelabelComponentImageFilter.h"
#include "itkImageRegionIterator.h"
#include "itkNumericTraits.h"
#include "itkProgressReporter.h"
#include "itkProgressTransformer.h"
#include "itkImageScanlineIterator.h"
#include <map>
#include <utility>

namespace itk
{
template <typename TInputImage, typename TOutputImage>
void
RelabelComponentImageFilter<TInputImage, TOutputImage>::GenerateInputRequestedRegion()
{
  // call the superclass' implementation of this method
  Superclass::GenerateInputRequestedRegion();

  // We need all the input.
  InputImagePointer input = const_cast<InputImageType *>(this->GetInput());
  if (input)
  {
    input->SetRequestedRegion(input->GetLargestPossibleRegion());
  }
}

template <typename TInputImage, typename TOutputImage>
void
RelabelComponentImageFilter<TInputImage, TOutputImage>::ParallelComputeLabels(const RegionType & inputRegionForThread)
{
  RelabelComponentObjectType initialSize;
  initialSize.m_SizeInPixels = 0;

  // walk the input
  ImageScanlineConstIterator<InputImageType> it(this->GetInput(), inputRegionForThread);

  MapType localSizeMap;

  auto mapIt = localSizeMap.end();
  while (!it.IsAtEnd())
  {
    while (!it.IsAtEndOfLine())
    {
      // Get the input pixel value
      const auto inputValue = it.Get();

      // if the input pixel is not the background
      if (inputValue != NumericTraits<LabelType>::ZeroValue())
      {
        // label is not currently in the map
        mapIt = localSizeMap.insert(mapIt, { inputValue, initialSize });

        // label is already in the map, update the values
        ++(mapIt->second.m_SizeInPixels);
      }

      // increment the iterator
      ++it;
    }
    it.NextLine();
  }

  // Merge localStatistics and m_LabelStatistics concurrently safe in a
  // local copy, this thread may do multiple merges.
  while (true)
  {
    std::unique_lock<std::mutex> lock(m_Mutex);

    if (m_SizeMap.empty())
    {
      swap(m_SizeMap, localSizeMap);
      break;
    }
    else
    {
      // copy the output map to thread local storage
      MapType toMerge;
      swap(m_SizeMap, toMerge);

      // allow other threads to merge data
      lock.unlock();

      // Merge toMerge into localSizeMap, locally
      for (auto & sizePair : toMerge)
      {
        localSizeMap[sizePair.first] += sizePair.second;
      }
    }
  } // release lock
}

template <typename TInputImage, typename TOutputImage>
void
RelabelComponentImageFilter<TInputImage, TOutputImage>::GenerateData()
{
  using LabelComponentPairType = std::pair<LabelType, RelabelComponentObjectType>;

  // Get the input and the output
  const TInputImage * input = this->GetInput();
  TOutputImage *      output = this->GetOutput();

  // Calculate the size of pixel
  float physicalPixelSize = 1.0;
  for (unsigned int i = 0; i < TInputImage::ImageDimension; ++i)
  {
    physicalPixelSize *= input->GetSpacing()[i];
  }

  // Walk the entire input image and compute used labels and the number of each label.
  ProgressTransformer progressTransformer1(0.0f, 0.5f, this);
  this->GetMultiThreader()->template ParallelizeImageRegion<ImageDimension>(
    input->GetRequestedRegion(),
    [this](const RegionType & inputRegion) { this->ParallelComputeLabels(inputRegion); },
    progressTransformer1.GetProcessObject());

  // Construct an array of the label, component information pair to sort
  auto sizeVector = std::vector<LabelComponentPairType>(m_SizeMap.begin(), m_SizeMap.end());

  // free memory by swapping to a default constructed object.
  MapType().swap(m_SizeMap);

  // Sort the objects by size by default, unless m_SortByObjectSize
  // is set to false.
  if (m_SortByObjectSize)
  {
    std::sort(sizeVector.begin(),
              sizeVector.end(),
              [](const LabelComponentPairType & a, const LabelComponentPairType & b) -> bool {
                return a.second.m_SizeInPixels > b.second.m_SizeInPixels ||
                       (!(a.second.m_SizeInPixels < b.second.m_SizeInPixels) && a.first < b.first);
              });
  }

  // A map from the input pixel labels to the output labels
  using RelabelMapType = std::map<LabelType, OutputPixelType>;
  RelabelMapType relabelMap;

  // create a lookup table to map the input label to the output label.
  // cache the object sizes for later access by the user
  m_NumberOfObjects = sizeVector.size();
  m_OriginalNumberOfObjects = sizeVector.size();
  m_SizeOfObjectsInPixels.clear();
  m_SizeOfObjectsInPixels.resize(m_NumberOfObjects);
  SizeValueType   NumberOfObjectsRemoved = 0;
  OutputPixelType outputLabel = 0;
  for (const auto & sizeVectorPair : sizeVector)
  {
    // skip objects that are too small ( but don't increment the output label )
    if (m_MinimumObjectSize > 0 && sizeVectorPair.second.m_SizeInPixels < m_MinimumObjectSize)
    {
      // map small objects to the background
      ++NumberOfObjectsRemoved;
      relabelMap.insert({ sizeVectorPair.first, NumericTraits<OutputPixelType>::ZeroValue() });
    }
    else
    {
      if (outputLabel == NumericTraits<OutputPixelType>::max())
      {
        itkExceptionMacro("Output range exceeded!");
      }
      // map for input labels to output labels (Note we use i+1 in the
      // map since index 0 is the background)
      relabelMap.insert({ sizeVectorPair.first, outputLabel + 1 });

      // cache object sizes for later access by the user
      m_SizeOfObjectsInPixels[outputLabel] = sizeVectorPair.second.m_SizeInPixels;
      ++outputLabel;
    }
  }

  // update number of objects and resize vectors if we have removed small
  // objects
  m_NumberOfObjects -= NumberOfObjectsRemoved;
  if (NumberOfObjectsRemoved > 0)
  {
    m_SizeOfObjectsInPixels.resize(m_NumberOfObjects);
  }

  // compute the object sizes in physical space too
  m_SizeOfObjectsInPhysicalUnits.resize(m_NumberOfObjects);
  std::transform(m_SizeOfObjectsInPixels.begin(),
                 m_SizeOfObjectsInPixels.end(),
                 m_SizeOfObjectsInPhysicalUnits.begin(),
                 [physicalPixelSize](ObjectSizeType sizeInPixels) { return sizeInPixels * physicalPixelSize; });

  // After the objects stats are computed add in the background label so the relabelMap can be directly applied.
  relabelMap.insert({ NumericTraits<LabelType>::ZeroValue(), NumericTraits<OutputPixelType>::ZeroValue() });

  // Second pass: walk just the output requested region and relabel
  // the necessary pixels.
  //

  // Allocate the output
  this->AllocateOutputs();

  // In parallel apply the relabling map
  ProgressTransformer progressTransformer2(0.5f, 1.0f, this);
  this->GetMultiThreader()->template ParallelizeImageRegion<ImageDimension>(
    output->GetRequestedRegion(),
    [this, &relabelMap](const RegionType & outputRegionForThread) {
      ImageScanlineIterator<OutputImageType>     oit(this->GetOutput(), outputRegionForThread);
      ImageScanlineConstIterator<InputImageType> it(this->GetInput(), outputRegionForThread);

      auto mapIt = relabelMap.cbegin();

      while (!oit.IsAtEnd())
      {
        while (!oit.IsAtEndOfLine())
        {
          const auto && inputValue = it.Get();

          if (mapIt->first != inputValue)
          {
            mapIt = relabelMap.find(inputValue);
          }

          // no new labels should be encountered in the input
          assert(mapIt != relabelMap.cend());

          oit.Set(mapIt->second);

          ++oit;
          ++it;
        }
        oit.NextLine();
        it.NextLine();
      }
    },
    progressTransformer2.GetProcessObject());
}

template <typename TInputImage, typename TOutputImage>
void
RelabelComponentImageFilter<TInputImage, TOutputImage>::PrintSelf(std::ostream & os, Indent indent) const
{
  Superclass::PrintSelf(os, indent);

  os << indent << "NumberOfObjects: " << m_NumberOfObjects << std::endl;
  os << indent << "OriginalNumberOfObjects: " << m_OriginalNumberOfObjects << std::endl;
  os << indent << "NumberOfObjectsToPrint: " << m_NumberOfObjectsToPrint << std::endl;
  os << indent << "MinimumObjectSizes: " << m_MinimumObjectSize << std::endl;
  os << indent << "SortByObjectSize: " << m_SortByObjectSize << std::endl;

  typename ObjectSizeInPixelsContainerType::const_iterator it;
  ObjectSizeInPhysicalUnitsContainerType::const_iterator   fit;
  SizeValueType                                            i;

  // limit the number of objects to print
  SizeValueType numPrint = std::min<SizeValueType>(m_NumberOfObjectsToPrint, m_SizeOfObjectsInPixels.size());

  for (i = 0, it = m_SizeOfObjectsInPixels.begin(), fit = m_SizeOfObjectsInPhysicalUnits.begin(); i < numPrint;
       ++it, ++fit, ++i)
  {
    os << indent << "Object #" << i + 1 << ": " << *it << " pixels, " << *fit << " physical units" << std::endl;
  }
  if (numPrint < m_SizeOfObjectsInPixels.size())
  {
    os << indent << "..." << std::endl;
  }
}
} // end namespace itk

#endif
<|endoftext|>


<commit_before>#ifndef STAN_MATH_REV_FUN_OWENS_T_HPP
#define STAN_MATH_REV_FUN_OWENS_T_HPP

#include <stan/math/rev/meta.hpp>
#include <stan/math/rev/core.hpp>
#include <stan/math/prim/fun/constants.hpp>
#include <stan/math/prim/fun/erf.hpp>
#include <stan/math/prim/fun/owens_t.hpp>
#include <stan/math/prim/fun/square.hpp>
#include <cmath>

namespace stan {
namespace math {

/**
 * The Owen's T function of h and a.
 *
 * Used to compute the cumulative density function for the skew normal
 * distribution.
 *
 * @tparam Var1 A scalar or Eigen type whose `scalar_type` is an var.
 * @tparam Var2 A scalar or Eigen type whose `scalar_type` is an var.
 * @param h var parameter.
 * @param a var parameter.
 * @return The Owen's T function.
 */
template <typename Var1, typename Var2,
          require_all_st_var<Var1, Var2>* = nullptr,
          require_all_not_std_vector_t<Var1, Var2>* = nullptr>
inline auto owens_t(const Var1& h, const Var2& a) {
  auto h_arena = to_arena(h);
  auto a_arena = to_arena(a);
  using return_type
      = return_var_matrix_t<decltype(owens_t(h_arena.val(), a_arena.val())),
                            Var1, Var2>;
  arena_t<return_type> ret = owens_t(h_arena.val(), a_arena.val());
  reverse_pass_callback([h_arena, a_arena, ret]() mutable {
    const auto& h_val = as_value_array_or_scalar(h_arena);
    const auto& a_val = as_value_array_or_scalar(a_arena);
    const auto neg_h_sq_div_2 = stan::math::eval(-square(h_val) * 0.5);
    const auto one_p_a_sq = stan::math::eval(1.0 + square(a_val));
    as_array_or_scalar(h_arena).adj() += possibly_sum<is_stan_scalar<Var1>>(
        as_array_or_scalar(ret.adj()) * erf(a_val * h_val * INV_SQRT_TWO)
        * exp(neg_h_sq_div_2) * INV_SQRT_TWO_PI * -0.5);
    as_array_or_scalar(a_arena).adj() += possibly_sum<is_stan_scalar<Var2>>(
        as_array_or_scalar(ret.adj()) * exp(neg_h_sq_div_2 * one_p_a_sq)
        / (one_p_a_sq * TWO_PI));
  });
  return return_type(ret);
}

/**
 * The Owen's T function of h and a.
 *
 * Used to compute the cumulative density function for the skew normal
 * distribution.
 *
 * @tparam Var A scalar or Eigen type whose `scalar_type` is an var.
 * @tparam Arith A scalar or Eigen type with an inner arirthmetic scalar value.
 * @param h var parameter.
 * @param a double parameter.
 * @return The Owen's T function.
 */
template <typename Var, typename Arith, require_st_arithmetic<Arith>* = nullptr,
          require_all_not_std_vector_t<Var, Arith>* = nullptr,
          require_st_var<Var>* = nullptr>
inline auto owens_t(const Var& h, const Arith& a) {
  auto h_arena = to_arena(h);
  auto a_arena = to_arena(a);
  using return_type
      = return_var_matrix_t<decltype(owens_t(h_arena.val(), a_arena)), Var,
                            Arith>;
  arena_t<return_type> ret = owens_t(h_arena.val(), a_arena);
  reverse_pass_callback([h_arena, a_arena, ret]() mutable {
    const auto& h_val = as_value_array_or_scalar(h_arena);
    as_array_or_scalar(h_arena).adj() += possibly_sum<is_stan_scalar<Var>>(
        as_array_or_scalar(ret.adj())
        * erf(as_array_or_scalar(a_arena) * h_val * INV_SQRT_TWO)
        * exp(-square(h_val) * 0.5) * INV_SQRT_TWO_PI * -0.5);
  });
  return return_type(ret);
}

/**
 * The Owen's T function of h and a.
 *
 * Used to compute the cumulative density function for the skew normal
 * distribution.
 *
 * @tparam Var A scalar or Eigen type whose `scalar_type` is an var.
 * @tparam Arith A scalar or Eigen type with an inner arithmetic scalar value.
 * @param h double parameter.
 * @param a var parameter.
 * @return The Owen's T function.
 */
template <typename Arith, typename Var, require_st_arithmetic<Arith>* = nullptr,
          require_all_not_std_vector_t<Var, Arith>* = nullptr,
          require_st_var<Var>* = nullptr>
inline auto owens_t(const Arith& h, const Var& a) {
  auto h_arena = to_arena(h);
  auto a_arena = to_arena(a);
  using return_type
      = return_var_matrix_t<decltype(owens_t(h_arena, a_arena.val())), Var,
                            Arith>;
  arena_t<return_type> ret = owens_t(h_arena, a_arena.val());
  reverse_pass_callback([h_arena, a_arena, ret]() mutable {
    const auto one_p_a_sq
        = eval(1.0 + square(as_value_array_or_scalar(a_arena)));
    as_array_or_scalar(a_arena).adj() += possibly_sum<is_stan_scalar<Var>>(
        as_array_or_scalar(ret.adj())
        * exp(-0.5 * square(as_array_or_scalar(h_arena)) * one_p_a_sq)
        / (one_p_a_sq * TWO_PI));
  });
  return return_type(ret);
}

}  // namespace math
}  // namespace stan
#endif
<commit_msg>add eval header to owens_t<commit_after>#ifndef STAN_MATH_REV_FUN_OWENS_T_HPP
#define STAN_MATH_REV_FUN_OWENS_T_HPP

#include <stan/math/rev/meta.hpp>
#include <stan/math/rev/core.hpp>
#include <stan/math/prim/fun/constants.hpp>
#include <stan/math/prim/fun/erf.hpp>
#include <stan/math/prim/fun/eval.hpp>
#include <stan/math/prim/fun/owens_t.hpp>
#include <stan/math/prim/fun/square.hpp>
#include <cmath>

namespace stan {
namespace math {

/**
 * The Owen's T function of h and a.
 *
 * Used to compute the cumulative density function for the skew normal
 * distribution.
 *
 * @tparam Var1 A scalar or Eigen type whose `scalar_type` is an var.
 * @tparam Var2 A scalar or Eigen type whose `scalar_type` is an var.
 * @param h var parameter.
 * @param a var parameter.
 * @return The Owen's T function.
 */
template <typename Var1, typename Var2,
          require_all_st_var<Var1, Var2>* = nullptr,
          require_all_not_std_vector_t<Var1, Var2>* = nullptr>
inline auto owens_t(const Var1& h, const Var2& a) {
  auto h_arena = to_arena(h);
  auto a_arena = to_arena(a);
  using return_type
      = return_var_matrix_t<decltype(owens_t(h_arena.val(), a_arena.val())),
                            Var1, Var2>;
  arena_t<return_type> ret = owens_t(h_arena.val(), a_arena.val());
  reverse_pass_callback([h_arena, a_arena, ret]() mutable {
    const auto& h_val = as_value_array_or_scalar(h_arena);
    const auto& a_val = as_value_array_or_scalar(a_arena);
    const auto neg_h_sq_div_2 = stan::math::eval(-square(h_val) * 0.5);
    const auto one_p_a_sq = stan::math::eval(1.0 + square(a_val));
    as_array_or_scalar(h_arena).adj() += possibly_sum<is_stan_scalar<Var1>>(
        as_array_or_scalar(ret.adj()) * erf(a_val * h_val * INV_SQRT_TWO)
        * exp(neg_h_sq_div_2) * INV_SQRT_TWO_PI * -0.5);
    as_array_or_scalar(a_arena).adj() += possibly_sum<is_stan_scalar<Var2>>(
        as_array_or_scalar(ret.adj()) * exp(neg_h_sq_div_2 * one_p_a_sq)
        / (one_p_a_sq * TWO_PI));
  });
  return return_type(ret);
}

/**
 * The Owen's T function of h and a.
 *
 * Used to compute the cumulative density function for the skew normal
 * distribution.
 *
 * @tparam Var A scalar or Eigen type whose `scalar_type` is an var.
 * @tparam Arith A scalar or Eigen type with an inner arirthmetic scalar value.
 * @param h var parameter.
 * @param a double parameter.
 * @return The Owen's T function.
 */
template <typename Var, typename Arith, require_st_arithmetic<Arith>* = nullptr,
          require_all_not_std_vector_t<Var, Arith>* = nullptr,
          require_st_var<Var>* = nullptr>
inline auto owens_t(const Var& h, const Arith& a) {
  auto h_arena = to_arena(h);
  auto a_arena = to_arena(a);
  using return_type
      = return_var_matrix_t<decltype(owens_t(h_arena.val(), a_arena)), Var,
                            Arith>;
  arena_t<return_type> ret = owens_t(h_arena.val(), a_arena);
  reverse_pass_callback([h_arena, a_arena, ret]() mutable {
    const auto& h_val = as_value_array_or_scalar(h_arena);
    as_array_or_scalar(h_arena).adj() += possibly_sum<is_stan_scalar<Var>>(
        as_array_or_scalar(ret.adj())
        * erf(as_array_or_scalar(a_arena) * h_val * INV_SQRT_TWO)
        * exp(-square(h_val) * 0.5) * INV_SQRT_TWO_PI * -0.5);
  });
  return return_type(ret);
}

/**
 * The Owen's T function of h and a.
 *
 * Used to compute the cumulative density function for the skew normal
 * distribution.
 *
 * @tparam Var A scalar or Eigen type whose `scalar_type` is an var.
 * @tparam Arith A scalar or Eigen type with an inner arithmetic scalar value.
 * @param h double parameter.
 * @param a var parameter.
 * @return The Owen's T function.
 */
template <typename Arith, typename Var, require_st_arithmetic<Arith>* = nullptr,
          require_all_not_std_vector_t<Var, Arith>* = nullptr,
          require_st_var<Var>* = nullptr>
inline auto owens_t(const Arith& h, const Var& a) {
  auto h_arena = to_arena(h);
  auto a_arena = to_arena(a);
  using return_type
      = return_var_matrix_t<decltype(owens_t(h_arena, a_arena.val())), Var,
                            Arith>;
  arena_t<return_type> ret = owens_t(h_arena, a_arena.val());
  reverse_pass_callback([h_arena, a_arena, ret]() mutable {
    const auto one_p_a_sq
        = eval(1.0 + square(as_value_array_or_scalar(a_arena)));
    as_array_or_scalar(a_arena).adj() += possibly_sum<is_stan_scalar<Var>>(
        as_array_or_scalar(ret.adj())
        * exp(-0.5 * square(as_array_or_scalar(h_arena)) * one_p_a_sq)
        / (one_p_a_sq * TWO_PI));
  });
  return return_type(ret);
}

}  // namespace math
}  // namespace stan
#endif
<|endoftext|>