<commit_before>/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.

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 "tensorflow/core/data/service/worker_impl.h"

#include "grpcpp/create_channel.h"
#include "absl/memory/memory.h"
#include "tensorflow/c/c_api_internal.h"
#include "tensorflow/c/tf_status_helper.h"
#include "tensorflow/core/data/dataset.pb.h"
#include "tensorflow/core/data/service/common.pb.h"
#include "tensorflow/core/data/service/credentials_factory.h"
#include "tensorflow/core/data/service/data_service.h"
#include "tensorflow/core/data/service/data_transfer.h"
#include "tensorflow/core/data/service/dispatcher.grpc.pb.h"
#include "tensorflow/core/data/service/dispatcher.pb.h"
#include "tensorflow/core/data/service/grpc_util.h"
#include "tensorflow/core/data/service/split_provider.h"
#include "tensorflow/core/data/service/task_runner.h"
#include "tensorflow/core/data/service/utils.h"
#include "tensorflow/core/data/standalone.h"
#include "tensorflow/core/framework/metrics.h"
#include "tensorflow/core/framework/tensor.pb.h"
#include "tensorflow/core/lib/core/errors.h"
#include "tensorflow/core/lib/io/zlib_outputbuffer.h"
#include "tensorflow/core/lib/monitoring/gauge.h"
#include "tensorflow/core/platform/errors.h"
#include "tensorflow/core/platform/refcount.h"
#include "tensorflow/core/platform/snappy.h"
#include "tensorflow/core/platform/thread_annotations.h"
#include "tensorflow/core/public/session_options.h"

namespace tensorflow {
namespace data {

const constexpr uint64 kRetryIntervalMicros = 5ull * 1000 * 1000;

namespace {
// Moves the element into the response. If the tensor contains a single
// CompressedElement variant, the move will be zero-copy. Otherwise, the tensor
// data will be serialized as TensorProtos.
Status MoveElementToResponse(std::vector<Tensor>&& element,
                             GetElementResponse& resp) {
  if (element.size() != 1 || element[0].dtype() != DT_VARIANT ||
      !TensorShapeUtils::IsScalar(element[0].shape())) {
    for (const auto& component : element) {
      UncompressedElement* uncompressed = resp.mutable_uncompressed();
      component.AsProtoTensorContent(uncompressed->add_components());
    }
    return Status::OK();
  }
  Variant& variant = element[0].scalar<Variant>()();
  CompressedElement* compressed = variant.get<CompressedElement>();
  if (compressed == nullptr) {
    return errors::FailedPrecondition(
        "Expected dataset to produce a CompressedElement variant tensor, but "
        "it produced ",
        variant.TypeName());
  }
  *resp.mutable_compressed() = *compressed;
  return Status::OK();
}
}  // namespace

DataServiceWorkerImpl::DataServiceWorkerImpl(
    const experimental::WorkerConfig& config)
    : config_(config) {
  metrics::RecordTFDataServiceWorkerCreated();
}

DataServiceWorkerImpl::~DataServiceWorkerImpl() {
  mutex_lock l(mu_);
  cancelled_ = true;
  task_completion_cv_.notify_one();
  heartbeat_cv_.notify_one();
}

Status DataServiceWorkerImpl::Start(const std::string& worker_address,
                                    const std::string& transfer_address) {
  VLOG(3) << "Starting tf.data service worker at address " << worker_address;
  worker_address_ = worker_address;
  transfer_address_ = transfer_address;

  dispatcher_ = absl::make_unique<DataServiceDispatcherClient>(
      config_.dispatcher_address(), config_.protocol());
  TF_RETURN_IF_ERROR(dispatcher_->Initialize());

  Status s = Heartbeat();
  while (!s.ok()) {
    if (!errors::IsUnavailable(s) && !errors::IsAborted(s) &&
        !errors::IsCancelled(s)) {
      return s;
    }
    LOG(WARNING) << "Failed to register with dispatcher at "
                 << config_.dispatcher_address() << ": " << s;
    Env::Default()->SleepForMicroseconds(kRetryIntervalMicros);
    s = Heartbeat();
  }
  LOG(INFO) << "Worker registered with dispatcher running at "
            << config_.dispatcher_address();
  task_completion_thread_ = absl::WrapUnique(
      Env::Default()->StartThread({}, "data-service-worker-task-completion",
                                  [this]() { TaskCompletionThread(); }));
  heartbeat_thread_ = absl::WrapUnique(Env::Default()->StartThread(
      {}, "data-service-worker-heartbeat", [this]() { HeartbeatThread(); }));
  mutex_lock l(mu_);
  registered_ = true;
  return Status::OK();
}
void DataServiceWorkerImpl::Stop() {
  {
    mutex_lock l(mu_);
    cancellation_manager_.StartCancel();
    cancelled_ = true;
    while (outstanding_requests_ > 0) {
      cv_.wait(l);
    }
  }
  // At this point there are no outstanding requests in this RPC handler.
  // However, requests successfully returned from this RPC handler may still be
  // in progress within the gRPC server. If we shut down the gRPC server
  // immediately, it could cause these requests to fail, e.g. with broken pipe.
  // To mitigate this, we sleep for some time to give the gRPC server time to
  // complete requests.
  Env::Default()->SleepForMicroseconds(config_.shutdown_quiet_period_ms() *
                                       1000);
}

Status DataServiceWorkerImpl::GetElementResult(
    const GetElementRequest* request, struct GetElementResult* result) {
  auto cleanup = gtl::MakeCleanup([&] {
    mutex_lock l(mu_);
    outstanding_requests_--;
    cv_.notify_all();
  });
  Task* task;
  {
    mutex_lock l(mu_);
    outstanding_requests_++;
    if (cancelled_) {
      return errors::Cancelled("Worker is shutting down");
    }
    if (!registered_) {
      // We need to reject requests until the worker has registered with the
      // dispatcher, so that we don't return NOT_FOUND for tasks that the worker
      // had before preemption.
      return errors::Unavailable(
          "Worker has not yet registered with dispatcher.");
    }
    auto it = tasks_.find(request->task_id());
    if (it == tasks_.end()) {
      if (finished_tasks_.contains(request->task_id())) {
        VLOG(3) << "Task is already finished";
        result->end_of_sequence = true;
        return Status::OK();
      } else {
        // Perhaps the workers hasn't gotten the task from the dispatcher yet.
        // Return Unavailable so that the client knows to continue retrying.
        return errors::Unavailable("Task ", request->task_id(), " not found");
      }
    }
    task = it->second.get();
    TF_RETURN_IF_ERROR(EnsureTaskInitialized(*task));
  }
  TF_RETURN_IF_ERROR(task->task_runner->GetNext(*request, *result));
  return Status::OK();
}

Status DataServiceWorkerImpl::ProcessTask(const ProcessTaskRequest* request,
                                          ProcessTaskResponse* response) {
  mutex_lock l(mu_);
  const TaskDef& task = request->task();
  VLOG(3) << "Received request to process task " << task.task_id();
  return ProcessTaskInternal(task);
}

Status DataServiceWorkerImpl::ProcessTaskInternal(const TaskDef& task_def)
    TF_EXCLUSIVE_LOCKS_REQUIRED(mu_) {
  std::shared_ptr<Task>& task = tasks_[task_def.task_id()];
  if (task) {
    VLOG(1) << "Received request to process already-processed task "
            << task->task_def.task_id();
    return Status::OK();
  }
  task = absl::make_unique<Task>(task_def);
  VLOG(3) << "Began processing for task " << task_def.task_id()
          << " with processing mode " << task_def.processing_mode();
  return Status::OK();
}

Status DataServiceWorkerImpl::EnsureTaskInitialized(
    DataServiceWorkerImpl::Task& task) {
  mutex_lock l(task.mu);
  if (task.initialized) {
    return Status::OK();
  }
  standalone::Dataset::Params params;
  std::unique_ptr<standalone::Dataset> dataset;
  std::unique_ptr<standalone::Iterator> iterator;

  switch (task.task_def.dataset_case()) {
    case TaskDef::kDatasetDef:
      TF_RETURN_IF_ERROR(standalone::Dataset::FromGraph(
          params, task.task_def.dataset_def().graph(), &dataset));
      break;
    case TaskDef::kPath: {
      DatasetDef def;
      Status s = ReadDatasetDef(task.task_def.path(), def);
      if (!s.ok()) {
        LOG(INFO) << "Failed to read dataset from " << task.task_def.path()
                  << ": " << s << ". Falling back to reading from dispatcher.";
        TF_RETURN_IF_ERROR(
            dispatcher_->GetDatasetDef(task.task_def.dataset_id(), def));
      }
      TF_RETURN_IF_ERROR(
          standalone::Dataset::FromGraph(params, def.graph(), &dataset));
      break;
    }
    case TaskDef::DATASET_NOT_SET:
      return errors::Internal("Unrecognized dataset case: ",
                              task.task_def.dataset_case());
  }
  switch (task.task_def.processing_mode()) {
    case DISTRIBUTED_EPOCH: {
      auto split_provider = absl::make_unique<DataServiceSplitProvider>(
          config_.dispatcher_address(), config_.protocol(),
          task.task_def.job_id(), config_.dispatcher_timeout_ms());
      TF_RETURN_IF_ERROR(
          dataset->MakeIterator(std::move(split_provider), &iterator));
      break;
    }
    case PARALLEL_EPOCHS:
      TF_RETURN_IF_ERROR(dataset->MakeIterator(&iterator));
      break;
    default:
      return errors::InvalidArgument("Unrecognized processing mode: ",
                                     task.task_def.processing_mode());
  }
  auto task_iterator = absl::make_unique<StandaloneTaskIterator>(
      std::move(dataset), std::move(iterator));
  TF_RETURN_IF_ERROR(
      TaskRunner::Create(config_, task.task_def, cancellation_manager_,
                         std::move(task_iterator), task.task_runner));

  task.initialized = true;
  VLOG(3) << "Created iterator for task " << task.task_def.task_id();
  return Status::OK();
}

Status DataServiceWorkerImpl::GetElement(const GetElementRequest* request,
                                         GetElementResponse* response) {
  VLOG(3) << "Received GetElement request for task " << request->task_id();
  struct GetElementResult result;
  TF_RETURN_IF_ERROR(GetElementResult(request, &result));
  response->set_end_of_sequence(result.end_of_sequence);
  response->set_skip_task(result.skip);
  if (response->end_of_sequence()) {
    mutex_lock l(mu_);
    VLOG(3) << "Reached end_of_sequence for task " << request->task_id();
    pending_completed_tasks_.insert(request->task_id());
    task_completion_cv_.notify_one();
  } else if (!response->skip_task()) {
    TF_RETURN_IF_ERROR(
        MoveElementToResponse(std::move(result.components), *response));
    VLOG(3) << "Producing an element for task " << request->task_id();
  }

  return Status::OK();
}

Status DataServiceWorkerImpl::GetWorkerTasks(
    const GetWorkerTasksRequest* request, GetWorkerTasksResponse* response) {
  mutex_lock l(mu_);
  for (const auto& it : tasks_) {
    Task* task = it.second.get();
    TaskInfo* task_info = response->add_tasks();
    task_info->set_worker_address(worker_address_);
    task_info->set_task_id(task->task_def.task_id());
    task_info->set_job_id(task->task_def.job_id());
  }
  return Status::OK();
}

void DataServiceWorkerImpl::TaskCompletionThread() TF_LOCKS_EXCLUDED(mu_) {
  while (true) {
    {
      mutex_lock l(mu_);
      while (!cancelled_ && pending_completed_tasks_.empty()) {
        task_completion_cv_.wait(l);
      }
      if (cancelled_) {
        VLOG(3) << "Task completion thread shutting down";
        return;
      }
    }
    Status s = SendTaskUpdates();
    if (!s.ok()) {
      LOG(WARNING) << "Failed to send task updates to dispatcher: " << s;
      mutex_lock l(mu_);
      if (!cancelled_) {
        task_completion_cv_.wait_for(
            l, std::chrono::microseconds(kRetryIntervalMicros));
      }
    }
  }
}

Status DataServiceWorkerImpl::SendTaskUpdates() TF_LOCKS_EXCLUDED(mu_) {
  std::vector<TaskProgress> task_progress;
  {
    mutex_lock l(mu_);
    VLOG(3) << "Sending " << pending_completed_tasks_.size()
            << " task updates to dispatcher";
    task_progress.reserve(pending_completed_tasks_.size());
    for (int task_id : pending_completed_tasks_) {
      task_progress.emplace_back();
      task_progress.back().set_task_id(task_id);
      task_progress.back().set_completed(true);
    }
  }

  TF_RETURN_IF_ERROR(dispatcher_->WorkerUpdate(worker_address_, task_progress));
  mutex_lock l(mu_);
  for (const auto& update : task_progress) {
    pending_completed_tasks_.erase(update.task_id());
  }
  VLOG(3) << "Sent " << task_progress.size() << " task updates ";
  return Status::OK();
}

void DataServiceWorkerImpl::HeartbeatThread() TF_LOCKS_EXCLUDED(mu_) {
  while (true) {
    int64 next_heartbeat_micros =
        Env::Default()->NowMicros() + (config_.heartbeat_interval_ms() * 1000);
    {
      mutex_lock l(mu_);
      while (!cancelled_ &&
             Env::Default()->NowMicros() < next_heartbeat_micros) {
        int64 time_to_wait_micros =
            next_heartbeat_micros - Env::Default()->NowMicros();
        heartbeat_cv_.wait_for(l,
                               std::chrono::microseconds(time_to_wait_micros));
      }
      if (cancelled_) {
        VLOG(3) << "Heartbeat thread shutting down";
        return;
      }
      if (!registered_) {
        VLOG(1) << "Not performing heartbeat; worker is not yet registered";
        continue;
      }
    }
    Status s = Heartbeat();
    if (!s.ok()) {
      LOG(WARNING) << "Failed to send heartbeat to dispatcher: " << s;
    }
  }
}

Status DataServiceWorkerImpl::Heartbeat() TF_LOCKS_EXCLUDED(mu_) {
  std::vector<int64> current_tasks;
  {
    mutex_lock l(mu_);
    for (const auto& task : tasks_) {
      current_tasks.push_back(task.first);
    }
  }
  std::vector<TaskDef> new_tasks;
  std::vector<int64> tasks_to_delete;
  TF_RETURN_IF_ERROR(
      dispatcher_->WorkerHeartbeat(worker_address_, transfer_address_,
                                   current_tasks, new_tasks, tasks_to_delete));
  mutex_lock l(mu_);
  for (const auto& task : new_tasks) {
    VLOG(1) << "Received new task from dispatcher with id " << task.task_id();
    Status s = ProcessTaskInternal(task);
    if (!s.ok() && !errors::IsAlreadyExists(s)) {
      LOG(WARNING) << "Failed to start processing task " << task.task_id()
                   << ": " << s;
    }
  }
  for (int64 task_id : tasks_to_delete) {
    VLOG(3) << "Deleting task " << task_id
            << " at the request of the dispatcher";
    tasks_.erase(task_id);
    finished_tasks_.insert(task_id);
  }
  return Status::OK();
}

}  // namespace data
}  // namespace tensorflow
<commit_msg>[tf.data service] Fix uninitialized field asan error.<commit_after>/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.

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 "tensorflow/core/data/service/worker_impl.h"

#include "grpcpp/create_channel.h"
#include "absl/memory/memory.h"
#include "tensorflow/c/c_api_internal.h"
#include "tensorflow/c/tf_status_helper.h"
#include "tensorflow/core/data/dataset.pb.h"
#include "tensorflow/core/data/service/common.pb.h"
#include "tensorflow/core/data/service/credentials_factory.h"
#include "tensorflow/core/data/service/data_service.h"
#include "tensorflow/core/data/service/data_transfer.h"
#include "tensorflow/core/data/service/dispatcher.grpc.pb.h"
#include "tensorflow/core/data/service/dispatcher.pb.h"
#include "tensorflow/core/data/service/grpc_util.h"
#include "tensorflow/core/data/service/split_provider.h"
#include "tensorflow/core/data/service/task_runner.h"
#include "tensorflow/core/data/service/utils.h"
#include "tensorflow/core/data/standalone.h"
#include "tensorflow/core/framework/metrics.h"
#include "tensorflow/core/framework/tensor.pb.h"
#include "tensorflow/core/lib/core/errors.h"
#include "tensorflow/core/lib/io/zlib_outputbuffer.h"
#include "tensorflow/core/lib/monitoring/gauge.h"
#include "tensorflow/core/platform/errors.h"
#include "tensorflow/core/platform/refcount.h"
#include "tensorflow/core/platform/snappy.h"
#include "tensorflow/core/platform/thread_annotations.h"
#include "tensorflow/core/public/session_options.h"

namespace tensorflow {
namespace data {

const constexpr uint64 kRetryIntervalMicros = 5ull * 1000 * 1000;

namespace {
// Moves the element into the response. If the tensor contains a single
// CompressedElement variant, the move will be zero-copy. Otherwise, the tensor
// data will be serialized as TensorProtos.
Status MoveElementToResponse(std::vector<Tensor>&& element,
                             GetElementResponse& resp) {
  if (element.size() != 1 || element[0].dtype() != DT_VARIANT ||
      !TensorShapeUtils::IsScalar(element[0].shape())) {
    for (const auto& component : element) {
      UncompressedElement* uncompressed = resp.mutable_uncompressed();
      component.AsProtoTensorContent(uncompressed->add_components());
    }
    return Status::OK();
  }
  Variant& variant = element[0].scalar<Variant>()();
  CompressedElement* compressed = variant.get<CompressedElement>();
  if (compressed == nullptr) {
    return errors::FailedPrecondition(
        "Expected dataset to produce a CompressedElement variant tensor, but "
        "it produced ",
        variant.TypeName());
  }
  *resp.mutable_compressed() = *compressed;
  return Status::OK();
}
}  // namespace

DataServiceWorkerImpl::DataServiceWorkerImpl(
    const experimental::WorkerConfig& config)
    : config_(config) {
  metrics::RecordTFDataServiceWorkerCreated();
}

DataServiceWorkerImpl::~DataServiceWorkerImpl() {
  mutex_lock l(mu_);
  cancelled_ = true;
  task_completion_cv_.notify_one();
  heartbeat_cv_.notify_one();
}

Status DataServiceWorkerImpl::Start(const std::string& worker_address,
                                    const std::string& transfer_address) {
  VLOG(3) << "Starting tf.data service worker at address " << worker_address;
  worker_address_ = worker_address;
  transfer_address_ = transfer_address;

  dispatcher_ = absl::make_unique<DataServiceDispatcherClient>(
      config_.dispatcher_address(), config_.protocol());
  TF_RETURN_IF_ERROR(dispatcher_->Initialize());

  Status s = Heartbeat();
  while (!s.ok()) {
    if (!errors::IsUnavailable(s) && !errors::IsAborted(s) &&
        !errors::IsCancelled(s)) {
      return s;
    }
    LOG(WARNING) << "Failed to register with dispatcher at "
                 << config_.dispatcher_address() << ": " << s;
    Env::Default()->SleepForMicroseconds(kRetryIntervalMicros);
    s = Heartbeat();
  }
  LOG(INFO) << "Worker registered with dispatcher running at "
            << config_.dispatcher_address();
  task_completion_thread_ = absl::WrapUnique(
      Env::Default()->StartThread({}, "data-service-worker-task-completion",
                                  [this]() { TaskCompletionThread(); }));
  heartbeat_thread_ = absl::WrapUnique(Env::Default()->StartThread(
      {}, "data-service-worker-heartbeat", [this]() { HeartbeatThread(); }));
  mutex_lock l(mu_);
  registered_ = true;
  return Status::OK();
}
void DataServiceWorkerImpl::Stop() {
  {
    mutex_lock l(mu_);
    cancellation_manager_.StartCancel();
    cancelled_ = true;
    while (outstanding_requests_ > 0) {
      cv_.wait(l);
    }
  }
  // At this point there are no outstanding requests in this RPC handler.
  // However, requests successfully returned from this RPC handler may still be
  // in progress within the gRPC server. If we shut down the gRPC server
  // immediately, it could cause these requests to fail, e.g. with broken pipe.
  // To mitigate this, we sleep for some time to give the gRPC server time to
  // complete requests.
  Env::Default()->SleepForMicroseconds(config_.shutdown_quiet_period_ms() *
                                       1000);
}

Status DataServiceWorkerImpl::GetElementResult(
    const GetElementRequest* request, struct GetElementResult* result) {
  auto cleanup = gtl::MakeCleanup([&] {
    mutex_lock l(mu_);
    outstanding_requests_--;
    cv_.notify_all();
  });
  Task* task;
  {
    mutex_lock l(mu_);
    outstanding_requests_++;
    if (cancelled_) {
      return errors::Cancelled("Worker is shutting down");
    }
    if (!registered_) {
      // We need to reject requests until the worker has registered with the
      // dispatcher, so that we don't return NOT_FOUND for tasks that the worker
      // had before preemption.
      return errors::Unavailable(
          "Worker has not yet registered with dispatcher.");
    }
    auto it = tasks_.find(request->task_id());
    if (it == tasks_.end()) {
      if (finished_tasks_.contains(request->task_id())) {
        VLOG(3) << "Task is already finished";
        result->end_of_sequence = true;
        result->skip = false;
        return Status::OK();
      } else {
        // Perhaps the workers hasn't gotten the task from the dispatcher yet.
        // Return Unavailable so that the client knows to continue retrying.
        return errors::Unavailable("Task ", request->task_id(), " not found");
      }
    }
    task = it->second.get();
    TF_RETURN_IF_ERROR(EnsureTaskInitialized(*task));
  }
  TF_RETURN_IF_ERROR(task->task_runner->GetNext(*request, *result));
  return Status::OK();
}

Status DataServiceWorkerImpl::ProcessTask(const ProcessTaskRequest* request,
                                          ProcessTaskResponse* response) {
  mutex_lock l(mu_);
  const TaskDef& task = request->task();
  VLOG(3) << "Received request to process task " << task.task_id();
  return ProcessTaskInternal(task);
}

Status DataServiceWorkerImpl::ProcessTaskInternal(const TaskDef& task_def)
    TF_EXCLUSIVE_LOCKS_REQUIRED(mu_) {
  std::shared_ptr<Task>& task = tasks_[task_def.task_id()];
  if (task) {
    VLOG(1) << "Received request to process already-processed task "
            << task->task_def.task_id();
    return Status::OK();
  }
  task = absl::make_unique<Task>(task_def);
  VLOG(3) << "Began processing for task " << task_def.task_id()
          << " with processing mode " << task_def.processing_mode();
  return Status::OK();
}

Status DataServiceWorkerImpl::EnsureTaskInitialized(
    DataServiceWorkerImpl::Task& task) {
  mutex_lock l(task.mu);
  if (task.initialized) {
    return Status::OK();
  }
  standalone::Dataset::Params params;
  std::unique_ptr<standalone::Dataset> dataset;
  std::unique_ptr<standalone::Iterator> iterator;

  switch (task.task_def.dataset_case()) {
    case TaskDef::kDatasetDef:
      TF_RETURN_IF_ERROR(standalone::Dataset::FromGraph(
          params, task.task_def.dataset_def().graph(), &dataset));
      break;
    case TaskDef::kPath: {
      DatasetDef def;
      Status s = ReadDatasetDef(task.task_def.path(), def);
      if (!s.ok()) {
        LOG(INFO) << "Failed to read dataset from " << task.task_def.path()
                  << ": " << s << ". Falling back to reading from dispatcher.";
        TF_RETURN_IF_ERROR(
            dispatcher_->GetDatasetDef(task.task_def.dataset_id(), def));
      }
      TF_RETURN_IF_ERROR(
          standalone::Dataset::FromGraph(params, def.graph(), &dataset));
      break;
    }
    case TaskDef::DATASET_NOT_SET:
      return errors::Internal("Unrecognized dataset case: ",
                              task.task_def.dataset_case());
  }
  switch (task.task_def.processing_mode()) {
    case DISTRIBUTED_EPOCH: {
      auto split_provider = absl::make_unique<DataServiceSplitProvider>(
          config_.dispatcher_address(), config_.protocol(),
          task.task_def.job_id(), config_.dispatcher_timeout_ms());
      TF_RETURN_IF_ERROR(
          dataset->MakeIterator(std::move(split_provider), &iterator));
      break;
    }
    case PARALLEL_EPOCHS:
      TF_RETURN_IF_ERROR(dataset->MakeIterator(&iterator));
      break;
    default:
      return errors::InvalidArgument("Unrecognized processing mode: ",
                                     task.task_def.processing_mode());
  }
  auto task_iterator = absl::make_unique<StandaloneTaskIterator>(
      std::move(dataset), std::move(iterator));
  TF_RETURN_IF_ERROR(
      TaskRunner::Create(config_, task.task_def, cancellation_manager_,
                         std::move(task_iterator), task.task_runner));

  task.initialized = true;
  VLOG(3) << "Created iterator for task " << task.task_def.task_id();
  return Status::OK();
}

Status DataServiceWorkerImpl::GetElement(const GetElementRequest* request,
                                         GetElementResponse* response) {
  VLOG(3) << "Received GetElement request for task " << request->task_id();
  struct GetElementResult result;
  TF_RETURN_IF_ERROR(GetElementResult(request, &result));
  response->set_end_of_sequence(result.end_of_sequence);
  response->set_skip_task(result.skip);
  if (response->end_of_sequence()) {
    mutex_lock l(mu_);
    VLOG(3) << "Reached end_of_sequence for task " << request->task_id();
    pending_completed_tasks_.insert(request->task_id());
    task_completion_cv_.notify_one();
  } else if (!response->skip_task()) {
    TF_RETURN_IF_ERROR(
        MoveElementToResponse(std::move(result.components), *response));
    VLOG(3) << "Producing an element for task " << request->task_id();
  }

  return Status::OK();
}

Status DataServiceWorkerImpl::GetWorkerTasks(
    const GetWorkerTasksRequest* request, GetWorkerTasksResponse* response) {
  mutex_lock l(mu_);
  for (const auto& it : tasks_) {
    Task* task = it.second.get();
    TaskInfo* task_info = response->add_tasks();
    task_info->set_worker_address(worker_address_);
    task_info->set_task_id(task->task_def.task_id());
    task_info->set_job_id(task->task_def.job_id());
  }
  return Status::OK();
}

void DataServiceWorkerImpl::TaskCompletionThread() TF_LOCKS_EXCLUDED(mu_) {
  while (true) {
    {
      mutex_lock l(mu_);
      while (!cancelled_ && pending_completed_tasks_.empty()) {
        task_completion_cv_.wait(l);
      }
      if (cancelled_) {
        VLOG(3) << "Task completion thread shutting down";
        return;
      }
    }
    Status s = SendTaskUpdates();
    if (!s.ok()) {
      LOG(WARNING) << "Failed to send task updates to dispatcher: " << s;
      mutex_lock l(mu_);
      if (!cancelled_) {
        task_completion_cv_.wait_for(
            l, std::chrono::microseconds(kRetryIntervalMicros));
      }
    }
  }
}

Status DataServiceWorkerImpl::SendTaskUpdates() TF_LOCKS_EXCLUDED(mu_) {
  std::vector<TaskProgress> task_progress;
  {
    mutex_lock l(mu_);
    VLOG(3) << "Sending " << pending_completed_tasks_.size()
            << " task updates to dispatcher";
    task_progress.reserve(pending_completed_tasks_.size());
    for (int task_id : pending_completed_tasks_) {
      task_progress.emplace_back();
      task_progress.back().set_task_id(task_id);
      task_progress.back().set_completed(true);
    }
  }

  TF_RETURN_IF_ERROR(dispatcher_->WorkerUpdate(worker_address_, task_progress));
  mutex_lock l(mu_);
  for (const auto& update : task_progress) {
    pending_completed_tasks_.erase(update.task_id());
  }
  VLOG(3) << "Sent " << task_progress.size() << " task updates ";
  return Status::OK();
}

void DataServiceWorkerImpl::HeartbeatThread() TF_LOCKS_EXCLUDED(mu_) {
  while (true) {
    int64 next_heartbeat_micros =
        Env::Default()->NowMicros() + (config_.heartbeat_interval_ms() * 1000);
    {
      mutex_lock l(mu_);
      while (!cancelled_ &&
             Env::Default()->NowMicros() < next_heartbeat_micros) {
        int64 time_to_wait_micros =
            next_heartbeat_micros - Env::Default()->NowMicros();
        heartbeat_cv_.wait_for(l,
                               std::chrono::microseconds(time_to_wait_micros));
      }
      if (cancelled_) {
        VLOG(3) << "Heartbeat thread shutting down";
        return;
      }
      if (!registered_) {
        VLOG(1) << "Not performing heartbeat; worker is not yet registered";
        continue;
      }
    }
    Status s = Heartbeat();
    if (!s.ok()) {
      LOG(WARNING) << "Failed to send heartbeat to dispatcher: " << s;
    }
  }
}

Status DataServiceWorkerImpl::Heartbeat() TF_LOCKS_EXCLUDED(mu_) {
  std::vector<int64> current_tasks;
  {
    mutex_lock l(mu_);
    for (const auto& task : tasks_) {
      current_tasks.push_back(task.first);
    }
  }
  std::vector<TaskDef> new_tasks;
  std::vector<int64> tasks_to_delete;
  TF_RETURN_IF_ERROR(
      dispatcher_->WorkerHeartbeat(worker_address_, transfer_address_,
                                   current_tasks, new_tasks, tasks_to_delete));
  mutex_lock l(mu_);
  for (const auto& task : new_tasks) {
    VLOG(1) << "Received new task from dispatcher with id " << task.task_id();
    Status s = ProcessTaskInternal(task);
    if (!s.ok() && !errors::IsAlreadyExists(s)) {
      LOG(WARNING) << "Failed to start processing task " << task.task_id()
                   << ": " << s;
    }
  }
  for (int64 task_id : tasks_to_delete) {
    VLOG(3) << "Deleting task " << task_id
            << " at the request of the dispatcher";
    tasks_.erase(task_id);
    finished_tasks_.insert(task_id);
  }
  return Status::OK();
}

}  // namespace data
}  // namespace tensorflow
<|endoftext|>


<commit_before>#pragma once

#include "helpers.hpp"
#include "priority_queue.hpp"

using std::pair;
using std::make_pair;

namespace search
{
    namespace ds
    {
        struct OldKeys : public unordered_map<Cell, Key, Cell::Hasher>
        {
            auto update_with(pair<Cell, Key> cell_key_pair)
            {
                erase(cell_key_pair.first);
                insert(cell_key_pair);
            }
        };

        class DStarCore
        {
            //
            //  Algorithm
            //

            auto validate(Cell c) const
            {
                return c.row >= 0 && c.row < (int)matrix.rows() && c.col >= 0 && c.col < (int)matrix.cols();
            }
            auto valid_neighbours_of(Cell c) const
            {
                Cells neighbours;
                for (auto direction = '1'; direction != '9'; ++direction)
                {
                    auto n = DIRECTIONS.at(direction)(c);
                    if (validate(n))
                        neighbours.insert(n);
                }
                return neighbours;
            }
            auto initialize()
            {
                q.reset();
                km = at(goal).r = 0;
                q.push(goal);
            }
            auto update_vertex(LpState& s)
            {
                if (s.cell != goal)
                {
                    auto minimum = huge();
                    for (auto neighbour : valid_neighbours_of(s.cell))
                        minimum = min(minimum, (at(neighbour).g + cost()));
                    s.r = minimum;
                }
                q.remove(s.cell);
                if (s.g != s.r)
                    q.push(s.cell), old_keys.update_with( { s.cell, Key{ s, km } } );
            }

            //
            //  helpers
            //

            auto at(Cell c) const -> LpState const&
            {
                return matrix.at(c);
            }
            auto at(Cell c) -> LpState&
            {
                return matrix.at(c);
            }
            auto mark_bad_cells(Cells const& bad_cells)
            {
                for (auto c : bad_cells) at(c).bad = true;
            }
            auto mark_h_values_with(Cell terminal)
            {
                auto mark_h = [=](Cell c) { at(c).h = hfunc(c, terminal); };
                matrix.each_cell(mark_h);
            }
            auto reset_statistics()
            {
                run_time = max_q_size = 0;
                expansions.clear(), path.clear();
            }

        public:
            //
            //  Constructor
            //
            DStarCore(unsigned rows, unsigned cols, Cell start, Cell goal, string heuristic, Cells const& bad_cells) :
                matrix{ rows, cols },
                start{ start },
                goal{ goal },
                hfunc{ HEURISTICS.at(heuristic) },
                km{ 0 },
                q{ [this](Cell l, Cell r) { return Key{ at(l), km } < Key{ at(r), km }; } },
                old_keys { }
            {
                mark_bad_cells(bad_cells);
                mark_h_values_with(start);  //h value : start to current
                reset_statistics();
            }
            //
            //  data members
            //
            Matrix matrix;
            Cell const start, goal;
            function<int(Cell, Cell)> const hfunc;
            int km;
            PriorityQueue < Cell, function<bool(Cell, Cell)> > q;
            OldKeys old_keys;
            //
            //  statistics
            //
            size_t max_q_size;
            Cells expansions;
            string path;
            long long run_time;
        };
    }
}
<commit_msg>add update_neighbours_of<commit_after>#pragma once

#include "helpers.hpp"
#include "priority_queue.hpp"

using std::pair;
using std::make_pair;

namespace search
{
    namespace ds
    {
        struct OldKeys : public unordered_map<Cell, Key, Cell::Hasher>
        {
            auto update_with(pair<Cell, Key> cell_key_pair)
            {
                erase(cell_key_pair.first);
                insert(cell_key_pair);
            }
        };

        class DStarCore
        {
            //
            //  Algorithm
            //

            auto validate(Cell c) const
            {
                return c.row >= 0 && c.row < (int)matrix.rows() && c.col >= 0 && c.col < (int)matrix.cols();
            }
            auto valid_neighbours_of(Cell c) const
            {
                Cells neighbours;
                for (auto direction = '1'; direction != '9'; ++direction)
                {
                    auto n = DIRECTIONS.at(direction)(c);
                    if (validate(n))
                        neighbours.insert(n);
                }
                return neighbours;
            }
            auto initialize()
            {
                q.reset();
                km = at(goal).r = 0;
                q.push(goal);
            }
            auto update_vertex(LpState& s)
            {
                if (s.cell != goal)
                {
                    auto minimum = huge();
                    for (auto neighbour : valid_neighbours_of(s.cell))
                        minimum = min(minimum, (at(neighbour).g + cost()));
                    s.r = minimum;
                }
                q.remove(s.cell);
                if (s.g != s.r)
                    q.push(s.cell), old_keys.update_with( { s.cell, Key{ s, km } } );
            }
            auto update_neighbours_of(Cell cell)
            {
                for (auto neighbour : valid_neighbours_of(cell))
                    if (!at(neighbour).bad)
                        update_vertex(at(neighbour));
            }

            //
            //  helpers
            //

            auto at(Cell c) const -> LpState const&
            {
                return matrix.at(c);
            }
            auto at(Cell c) -> LpState&
            {
                return matrix.at(c);
            }
            auto mark_bad_cells(Cells const& bad_cells)
            {
                for (auto c : bad_cells) at(c).bad = true;
            }
            auto mark_h_values_with(Cell terminal)
            {
                auto mark_h = [=](Cell c) { at(c).h = hfunc(c, terminal); };
                matrix.each_cell(mark_h);
            }
            auto reset_statistics()
            {
                run_time = max_q_size = 0;
                expansions.clear(), path.clear();
            }

        public:
            //
            //  Constructor
            //
            DStarCore(unsigned rows, unsigned cols, Cell start, Cell goal, string heuristic, Cells const& bad_cells) :
                matrix{ rows, cols },
                start{ start },
                goal{ goal },
                hfunc{ HEURISTICS.at(heuristic) },
                km{ 0 },
                q{ [this](Cell l, Cell r) { return Key{ at(l), km } < Key{ at(r), km }; } },
                old_keys { }
            {
                mark_bad_cells(bad_cells);
                mark_h_values_with(start);  //h value : start to current
                reset_statistics();
            }
            //
            //  data members
            //
            Matrix matrix;
            Cell const start, goal;
            function<int(Cell, Cell)> const hfunc;
            int km;
            PriorityQueue < Cell, function<bool(Cell, Cell)> > q;
            OldKeys old_keys;
            //
            //  statistics
            //
            size_t max_q_size;
            Cells expansions;
            string path;
            long long run_time;
        };
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2009 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/browser/login_prompt.h"

#include "app/l10n_util.h"
#include "base/command_line.h"
#include "base/lock.h"
#include "chrome/browser/chrome_thread.h"
#include "chrome/browser/password_manager/password_manager.h"
#include "chrome/browser/renderer_host/render_process_host.h"
#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
#include "chrome/browser/renderer_host/resource_dispatcher_host_request_info.h"
#include "chrome/browser/tab_contents/constrained_window.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/chrome_switches.h"
#include "grit/generated_resources.h"
#include "net/base/auth.h"
#include "net/url_request/url_request.h"

using webkit_glue::PasswordForm;

class LoginHandlerImpl;

// Helper to remove the ref from an URLRequest to the LoginHandler.
// Should only be called from the IO thread, since it accesses an URLRequest.
void ResetLoginHandlerForRequest(URLRequest* request) {
  ResourceDispatcherHostRequestInfo* info =
      ResourceDispatcherHost::InfoForRequest(request);
  if (!info)
    return;

  info->set_login_handler(NULL);
}

// Get the signon_realm under which this auth info should be stored.
//
// The format of the signon_realm for proxy auth is:
//     proxy-host/auth-realm
// The format of the signon_realm for server auth is:
//     url-scheme://url-host[:url-port]/auth-realm
//
// Be careful when changing this function, since you could make existing
// saved logins un-retrievable.
std::string GetSignonRealm(const GURL& url,
                           const net::AuthChallengeInfo& auth_info) {
  std::string signon_realm;
  if (auth_info.is_proxy) {
    signon_realm = WideToASCII(auth_info.host_and_port);
    signon_realm.append("/");
  } else {
    // Take scheme, host, and port from the url.
    signon_realm = url.GetOrigin().spec();
    // This ends with a "/".
  }
  signon_realm.append(WideToUTF8(auth_info.realm));
  return signon_realm;
}

// ----------------------------------------------------------------------------
// LoginDialogTask

// This task is run on the UI thread and creates a constrained window with
// a LoginView to prompt the user.  The response will be sent to LoginHandler,
// which then routes it to the URLRequest on the I/O thread.
class LoginDialogTask : public Task {
 public:
  LoginDialogTask(net::AuthChallengeInfo* auth_info, LoginHandler* handler)
      : auth_info_(auth_info), handler_(handler) {
  }
  virtual ~LoginDialogTask() {
  }

  void Run() {
    TabContents* parent_contents = handler_->GetTabContentsForLogin();
    if (!parent_contents) {
      // The request was probably cancelled.
      return;
    }

    // Tell the password manager to look for saved passwords.
    PasswordManager* password_manager =
        parent_contents->GetPasswordManager();
    std::vector<PasswordForm> v;
    MakeInputForPasswordManager(parent_contents->GetURL(), &v);
    password_manager->PasswordFormsSeen(v);
    handler_->SetPasswordManager(password_manager);

    std::wstring explanation = auth_info_->realm.empty() ?
        l10n_util::GetStringF(IDS_LOGIN_DIALOG_DESCRIPTION_NO_REALM,
                              auth_info_->host_and_port) :
        l10n_util::GetStringF(IDS_LOGIN_DIALOG_DESCRIPTION,
                              auth_info_->host_and_port,
                              auth_info_->realm);
    handler_->BuildViewForPasswordManager(password_manager,
                                          explanation);
  }

 private:
  // Helper to create a PasswordForm and stuff it into a vector as input
  // for PasswordManager::PasswordFormsSeen, the hook into PasswordManager.
  void MakeInputForPasswordManager(
      const GURL& origin_url,
      std::vector<PasswordForm>* password_manager_input) {
    PasswordForm dialog_form;
    if (LowerCaseEqualsASCII(auth_info_->scheme, "basic")) {
      dialog_form.scheme = PasswordForm::SCHEME_BASIC;
    } else if (LowerCaseEqualsASCII(auth_info_->scheme, "digest")) {
      dialog_form.scheme = PasswordForm::SCHEME_DIGEST;
    } else {
      dialog_form.scheme = PasswordForm::SCHEME_OTHER;
    }
    if (auth_info_->is_proxy) {
      std::string origin = WideToASCII(auth_info_->host_and_port);
      // We don't expect this to already start with http:// or https://.
      DCHECK(origin.find("http://") != 0 && origin.find("https://") != 0);
      origin = std::string("http://") + origin;
      dialog_form.origin = GURL(origin);
    } else {
      dialog_form.origin = origin_url;
    }
    dialog_form.signon_realm = GetSignonRealm(dialog_form.origin, *auth_info_);
    password_manager_input->push_back(dialog_form);
    // Set the password form for the handler (by copy).
    handler_->SetPasswordForm(dialog_form);
  }

  // Info about who/where/what is asking for authentication.
  scoped_refptr<net::AuthChallengeInfo> auth_info_;

  // Where to send the authentication when obtained.
  // This is owned by the ResourceDispatcherHost that invoked us.
  LoginHandler* handler_;

  DISALLOW_EVIL_CONSTRUCTORS(LoginDialogTask);
};

// ----------------------------------------------------------------------------
// Public API

LoginHandler* CreateLoginPrompt(net::AuthChallengeInfo* auth_info,
                                URLRequest* request) {
  LoginHandler* handler = LoginHandler::Create(request);
  ChromeThread::PostTask(
      ChromeThread::UI, FROM_HERE, new LoginDialogTask(auth_info, handler));
  return handler;
}
<commit_msg>fix 32718<commit_after>// Copyright (c) 2009 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/browser/login_prompt.h"

#include "app/l10n_util.h"
#include "base/command_line.h"
#include "base/lock.h"
#include "chrome/browser/chrome_thread.h"
#include "chrome/browser/password_manager/password_manager.h"
#include "chrome/browser/renderer_host/render_process_host.h"
#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
#include "chrome/browser/renderer_host/resource_dispatcher_host_request_info.h"
#include "chrome/browser/tab_contents/constrained_window.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/chrome_switches.h"
#include "grit/generated_resources.h"
#include "net/base/auth.h"
#include "net/base/net_util.h"
#include "net/url_request/url_request.h"

using webkit_glue::PasswordForm;

class LoginHandlerImpl;

// Helper to remove the ref from an URLRequest to the LoginHandler.
// Should only be called from the IO thread, since it accesses an URLRequest.
void ResetLoginHandlerForRequest(URLRequest* request) {
  ResourceDispatcherHostRequestInfo* info =
      ResourceDispatcherHost::InfoForRequest(request);
  if (!info)
    return;

  info->set_login_handler(NULL);
}

// Get the signon_realm under which this auth info should be stored.
//
// The format of the signon_realm for proxy auth is:
//     proxy-host/auth-realm
// The format of the signon_realm for server auth is:
//     url-scheme://url-host[:url-port]/auth-realm
//
// Be careful when changing this function, since you could make existing
// saved logins un-retrievable.
std::string GetSignonRealm(const GURL& url,
                           const net::AuthChallengeInfo& auth_info) {
  std::string signon_realm;
  if (auth_info.is_proxy) {
    signon_realm = WideToASCII(auth_info.host_and_port);
    signon_realm.append("/");
  } else {
    // Take scheme, host, and port from the url.
    signon_realm = url.GetOrigin().spec();
    // This ends with a "/".
  }
  signon_realm.append(WideToUTF8(auth_info.realm));
  return signon_realm;
}

// ----------------------------------------------------------------------------
// LoginDialogTask

// This task is run on the UI thread and creates a constrained window with
// a LoginView to prompt the user.  The response will be sent to LoginHandler,
// which then routes it to the URLRequest on the I/O thread.
class LoginDialogTask : public Task {
 public:
  LoginDialogTask(const GURL& request_url,
                  net::AuthChallengeInfo* auth_info,
                  LoginHandler* handler)
      : request_url_(request_url), auth_info_(auth_info), handler_(handler) {
  }
  virtual ~LoginDialogTask() {
  }

  void Run() {
    TabContents* parent_contents = handler_->GetTabContentsForLogin();
    if (!parent_contents) {
      // The request was probably cancelled.
      return;
    }

    // Tell the password manager to look for saved passwords.
    PasswordManager* password_manager =
        parent_contents->GetPasswordManager();
    std::vector<PasswordForm> v;
    MakeInputForPasswordManager(&v);
    password_manager->PasswordFormsSeen(v);
    handler_->SetPasswordManager(password_manager);

    std::wstring explanation = auth_info_->realm.empty() ?
        l10n_util::GetStringF(IDS_LOGIN_DIALOG_DESCRIPTION_NO_REALM,
                              auth_info_->host_and_port) :
        l10n_util::GetStringF(IDS_LOGIN_DIALOG_DESCRIPTION,
                              auth_info_->host_and_port,
                              auth_info_->realm);
    handler_->BuildViewForPasswordManager(password_manager,
                                          explanation);
  }

 private:
  // Helper to create a PasswordForm and stuff it into a vector as input
  // for PasswordManager::PasswordFormsSeen, the hook into PasswordManager.
  void MakeInputForPasswordManager(
      std::vector<PasswordForm>* password_manager_input) {
    PasswordForm dialog_form;
    if (LowerCaseEqualsASCII(auth_info_->scheme, "basic")) {
      dialog_form.scheme = PasswordForm::SCHEME_BASIC;
    } else if (LowerCaseEqualsASCII(auth_info_->scheme, "digest")) {
      dialog_form.scheme = PasswordForm::SCHEME_DIGEST;
    } else {
      dialog_form.scheme = PasswordForm::SCHEME_OTHER;
    }
    std::string host_and_port(WideToASCII(auth_info_->host_and_port));
    if (net::GetHostAndPort(request_url_) != host_and_port) {
      dialog_form.origin = GURL();
      NOTREACHED();
    } else if (auth_info_->is_proxy) {
      std::string origin = host_and_port;
      // We don't expect this to already start with http:// or https://.
      DCHECK(origin.find("http://") != 0 && origin.find("https://") != 0);
      origin = std::string("http://") + origin;
      dialog_form.origin = GURL(origin);
    } else {
      dialog_form.origin = GURL(request_url_.scheme() + "://" + host_and_port);
    }
    dialog_form.signon_realm = GetSignonRealm(dialog_form.origin, *auth_info_);
    password_manager_input->push_back(dialog_form);
    // Set the password form for the handler (by copy).
    handler_->SetPasswordForm(dialog_form);
  }

  // The url from the URLRequest initiating the auth challenge.
  GURL request_url_;

  // Info about who/where/what is asking for authentication.
  scoped_refptr<net::AuthChallengeInfo> auth_info_;

  // Where to send the authentication when obtained.
  // This is owned by the ResourceDispatcherHost that invoked us.
  LoginHandler* handler_;

  DISALLOW_EVIL_CONSTRUCTORS(LoginDialogTask);
};

// ----------------------------------------------------------------------------
// Public API

LoginHandler* CreateLoginPrompt(net::AuthChallengeInfo* auth_info,
                                URLRequest* request) {
  LoginHandler* handler = LoginHandler::Create(request);
  ChromeThread::PostTask(
      ChromeThread::UI, FROM_HERE, new LoginDialogTask(
          request->url(), auth_info, handler));
  return handler;
}
<|endoftext|>


<commit_before>//Copyright (c) 2018 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.

#include "LinePolygonsCrossings.h"

#include <algorithm>

#include "../utils/polygonUtils.h"
#include "../sliceDataStorage.h"
#include "../utils/SVG.h"

namespace cura {

LinePolygonsCrossings::Crossing::Crossing(const coord_t x, const size_t point_idx)
: x(x)
, point_idx(point_idx)
{
}

LinePolygonsCrossings::PolyCrossings::PolyCrossings(const size_t poly_idx)
: poly_idx(poly_idx)
, min(std::numeric_limits<coord_t>::max(), NO_INDEX), max(std::numeric_limits<coord_t>::min(), NO_INDEX)
, n_crossings(0)
{
}

bool LinePolygonsCrossings::calcScanlineCrossings(bool fail_on_unavoidable_obstacles)
{
    min_crossing_idx = NO_INDEX;
    max_crossing_idx = NO_INDEX;

    for(unsigned int poly_idx = 0; poly_idx < boundary.size(); poly_idx++)
    {
        PolyCrossings minMax(poly_idx);
        ConstPolygonRef poly = boundary[poly_idx];
        Point p0 = transformation_matrix.apply(poly[poly.size() - 1]);
        for(unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
        {
            Point p1 = transformation_matrix.apply(poly[point_idx]);
            if ((p0.Y >= transformed_startPoint.Y && p1.Y <= transformed_startPoint.Y) || (p1.Y >= transformed_startPoint.Y && p0.Y <= transformed_startPoint.Y))
            { // if line segment crosses the line through the transformed start and end point (aka scanline)
                if (p1.Y == p0.Y) //Line segment is parallel with the scanline. That means that both endpoints lie on the scanline, so they will have intersected with the adjacent line.
                {
                    p0 = p1;
                    continue;
                }
                const coord_t x = p0.X + (p1.X - p0.X) * (transformed_startPoint.Y - p0.Y) / (p1.Y - p0.Y); // intersection point between line segment and the scanline

                if (x >= transformed_startPoint.X && x <= transformed_endPoint.X)
                {
                    if (!((p1.Y == transformed_startPoint.Y && p1.Y < p0.Y) || (p0.Y == transformed_startPoint.Y && p0.Y < p1.Y)))
                    { // perform edge case only for line segments on and below the scanline, not for line segments on and above.
                        // \/ will be no crossings and /\ two, but most importantly | will be one crossing.
                        minMax.n_crossings++;
                    }
                    if(x < minMax.min.x) //For the leftmost intersection, move x left to stay outside of the border.
                                         //Note: The actual distance from the intersection to the border is almost always less than dist_to_move_boundary_point_outside, since it only moves along the direction of the scanline.
                    {
                        minMax.min.x = x;
                        minMax.min.point_idx = point_idx;
                    }
                    if(x > minMax.max.x) //For the rightmost intersection, move x right to stay outside of the border.
                    {
                        minMax.max.x = x;
                        minMax.max.point_idx = point_idx;
                    }
                }
            }
            p0 = p1;
        }

        if (fail_on_unavoidable_obstacles && minMax.n_crossings % 2 == 1)
        { // if start area and end area are not the same
            return false;
        }
        else if (minMax.min.point_idx != NO_INDEX) // then always also max.point_idx != NO_INDEX
        { // if this polygon crossed the scanline
            if (min_crossing_idx == NO_INDEX || minMax.min.x < crossings[min_crossing_idx].min.x) { min_crossing_idx = crossings.size(); }
            if (max_crossing_idx == NO_INDEX || minMax.max.x > crossings[max_crossing_idx].max.x) { max_crossing_idx = crossings.size(); }
            crossings.push_back(minMax);
        }
    }
    return true;
}

bool LinePolygonsCrossings::lineSegmentCollidesWithBoundary()
{
    Point diff = endPoint - startPoint;

    transformation_matrix = PointMatrix(diff);
    transformed_startPoint = transformation_matrix.apply(startPoint);
    transformed_endPoint = transformation_matrix.apply(endPoint);

    for(ConstPolygonRef poly : boundary)
    {
        Point p0 = transformation_matrix.apply(poly.back());
        for(Point p1_ : poly)
        {
            Point p1 = transformation_matrix.apply(p1_);
            // when the boundary just touches the line don't disambiguate between the boundary moving on to actually cross the line
            // and the boundary bouncing back, resulting in not a real collision - to keep the algorithm simple.
            //
            // disregard overlapping line segments; probably the next or previous line segment is not overlapping, but will give a collision
            // when the boundary line segment fully overlaps with the line segment this edge case is not viewed as a collision
            if (p1.Y != p0.Y && ((p0.Y >= transformed_startPoint.Y && p1.Y <= transformed_startPoint.Y) || (p1.Y >= transformed_startPoint.Y && p0.Y <= transformed_startPoint.Y)))
            {
                int64_t x = p0.X + (p1.X - p0.X) * (transformed_startPoint.Y - p0.Y) / (p1.Y - p0.Y);

                if (x > transformed_startPoint.X && x < transformed_endPoint.X)
                {
                    return true;
                }
            }
            p0 = p1;
        }
    }

    return false;
}

bool LinePolygonsCrossings::generateCombingPath(CombPath& combPath, int64_t max_comb_distance_ignored, bool fail_on_unavoidable_obstacles)
{
    if (shorterThen(endPoint - startPoint, max_comb_distance_ignored) || !lineSegmentCollidesWithBoundary())
    {
        //We're not crossing any boundaries. So skip the comb generation.
        combPath.push_back(startPoint);
        combPath.push_back(endPoint);
        return true;
    }

    bool success = calcScanlineCrossings(fail_on_unavoidable_obstacles);
    if (!success)
    {
        return false;
    }

    CombPath basicPath;
    generateBasicCombingPath(basicPath);
    optimizePath(basicPath, combPath);
//     combPath = basicPath; // uncomment to disable comb path optimization
    return true;
}

void LinePolygonsCrossings::generateBasicCombingPath(CombPath& combPath)
{
    for (PolyCrossings* crossing = getNextPolygonAlongScanline(transformed_startPoint.X)
        ; crossing != nullptr
        ; crossing = getNextPolygonAlongScanline(crossing->max.x))
    {
        generateBasicCombingPath(*crossing, combPath);
    }
    combPath.push_back(endPoint);
}

void LinePolygonsCrossings::generateBasicCombingPath(PolyCrossings& polyCrossings, CombPath& combPath)
{
    // minimise the path length by measuring the length of both paths around the polygon so we can determine the shorter path

    ConstPolygonRef poly = boundary[polyCrossings.poly_idx];
    combPath.push_back(transformation_matrix.unapply(Point(polyCrossings.min.x - std::abs(dist_to_move_boundary_point_outside), transformed_startPoint.Y)));

    // follow the path in the same direction as the winding order of the boundary polygon
    std::vector<Point> fwd_points;
    Point prev = combPath.back();
    coord_t fwd_len = 0;
    for (unsigned int point_idx = polyCrossings.min.point_idx
        ; point_idx != polyCrossings.max.point_idx
        ; point_idx = (point_idx < poly.size() - 1) ? (point_idx + 1) : (0))
    {
        const Point p = PolygonUtils::getBoundaryPointWithOffset(poly, point_idx, dist_to_move_boundary_point_outside);
        fwd_points.push_back(p);
        fwd_len += vSize(p - prev);
        prev = p;
    }

    // follow the path in the opposite direction of the winding order of the boundary polygon
    std::vector<Point> rev_points;
    prev = combPath.back();
    coord_t rev_len = 0;
    unsigned int min_idx = (polyCrossings.min.point_idx == 0)? poly.size() - 1 : polyCrossings.min.point_idx - 1;
    unsigned int max_idx = (polyCrossings.max.point_idx == 0)? poly.size() - 1 : polyCrossings.max.point_idx - 1;
    for (unsigned int point_idx = min_idx
        ; point_idx != max_idx
        ; point_idx = (point_idx > 0) ? (point_idx - 1) : (poly.size() - 1))
    {
        const Point p = PolygonUtils::getBoundaryPointWithOffset(poly, point_idx, dist_to_move_boundary_point_outside);
        rev_points.push_back(p);
        rev_len += vSize(p - prev);
        prev = p;
        if (rev_len > fwd_len)
        {
            // this path is already longer than the forward path so there's no point in carrying on
            break;
        }
    }

    const Point last = transformation_matrix.unapply(Point(polyCrossings.max.x + std::abs(dist_to_move_boundary_point_outside), transformed_startPoint.Y));

    if (fwd_points.size() > 0)
    {
        fwd_len += vSize(last - fwd_points.back());
    }
    if (rev_points.size() > 0)
    {
        rev_len += vSize(last - rev_points.back());
    }

    // use the points from the shortest path
    for (auto& p : (fwd_len < rev_len) ? fwd_points : rev_points)
    {
        combPath.push_back(p);
    }
    combPath.push_back(last);
}

LinePolygonsCrossings::PolyCrossings* LinePolygonsCrossings::getNextPolygonAlongScanline(int64_t x)
{
    PolyCrossings* ret = nullptr;
    for(PolyCrossings& crossing : crossings)
    {
        if (crossing.min.x > x && (ret == nullptr || crossing.min.x < ret->min.x) )
        {
            ret = &crossing;
        }
    }
    return ret;
}

bool LinePolygonsCrossings::optimizePath(CombPath& comb_path, CombPath& optimized_comb_path)
{
    optimized_comb_path.push_back(startPoint);
    for(unsigned int point_idx = 1; point_idx<comb_path.size(); point_idx++)
    {
        if(comb_path[point_idx] == comb_path[point_idx - 1]) //Two points are the same. Skip the second.
        {
            continue;
        }
        Point& current_point = optimized_comb_path.back();
        if (PolygonUtils::polygonCollidesWithLineSegment(current_point, comb_path[point_idx], loc_to_line_grid))
        {
            if (PolygonUtils::polygonCollidesWithLineSegment(current_point, comb_path[point_idx - 1], loc_to_line_grid))
            {
                comb_path.cross_boundary = true;
            }
            // before we add this point, can we discard some of the previous points and still avoid clashing with the combing boundary?
            const int max_short_circuit_len = 1 << 3; // test distances of 8, 4, 2, 1
            for (unsigned n = std::min(max_short_circuit_len, (int)optimized_comb_path.size()); n > 0; n >>= 1)
            {
                if (optimized_comb_path.size() > n && !PolygonUtils::polygonCollidesWithLineSegment(optimized_comb_path[optimized_comb_path.size() - n - 1], comb_path[point_idx - 1], loc_to_line_grid))
                {
                    // we can remove n points from the path without it clashing with the combing boundary
                    for (unsigned i = 0; i < n; ++i)
                    {
                        optimized_comb_path.pop_back();
                    }
                    break;
                }
            }
            optimized_comb_path.push_back(comb_path[point_idx - 1]);

            if (point_idx == 1)
            {
                // we have just inserted the first point in the path that touches the boundary
                // and as the first line in the comb is pointing from the start point towards the
                // end point, we can maybe save some travel by replacing this first point with another
                // that also lies on the second path of the comb, like this:
                //
                //         1
                //        /|
                //       / |
                //      /  |           ===>  ----1
                //     S   |                 S   |
                //         |                     |
                //         2----3 ...            2----3 ...
                //

                Point p = optimized_comb_path.back();
                for (float frac : { 0.9, 0.9, 0.7, 0.5 })
                {
                    // slide p towards the second point in the comb path
                    p = comb_path[1] + (p - comb_path[1]) * frac;
                    if (!PolygonUtils::polygonCollidesWithLineSegment(startPoint, p, loc_to_line_grid))
                    {
                        // using the new corner doesn't cause a conflict
                        optimized_comb_path.back() = p;
                    }
                    else
                    {
                        // quit loop and keep what we know to be good
                        break;
                    }
                }
            }
        }
        else
        {
            // : dont add the newest point

            // TODO: add the below extra optimization? (+/- 7% extra computation time, +/- 2% faster print for Dual_extrusion_support_generation.stl)
            while (optimized_comb_path.size() > 1)
            {
                if (PolygonUtils::polygonCollidesWithLineSegment(optimized_comb_path[optimized_comb_path.size() - 2], comb_path[point_idx], loc_to_line_grid))
                {
                    break;
                }
                else
                {
                    optimized_comb_path.pop_back();
                }
            }
        }
    }

    if (optimized_comb_path.size() > 1)
    {
        const unsigned n = optimized_comb_path.size();
        // the penultimate corner may be deleted if the resulting path doesn't conflict with the boundary
        if (!PolygonUtils::polygonCollidesWithLineSegment(optimized_comb_path[n - 2], comb_path.back(), loc_to_line_grid))
        {
            optimized_comb_path.pop_back();
        }
        else {
            // that wasn't possible so try and move the penultimate corner without conficting with the boundary
            // in exactly the same way as we did at the start of the path
            for (float frac : { 0.9, 0.9, 0.7, 0.5 })
            {
                // make a new point between the penultimate corner and the corner before that
                Point p = optimized_comb_path[n - 2] + (optimized_comb_path[n - 1] - optimized_comb_path[n - 2]) * frac;
                if (!PolygonUtils::polygonCollidesWithLineSegment(p, comb_path.back(), loc_to_line_grid))
                {
                    // using the new corner doesn't cause a conflict
                    optimized_comb_path[n - 1] = p;
                }
                else
                {
                    // quit loop and keep what we know to be good
                    break;
                }
            }
        }
    }

    optimized_comb_path.push_back(comb_path.back());
    return true;
}

}//namespace cura
<commit_msg>Bug fix in comb path generation - correct fwd path length before comparing with rev path length.<commit_after>//Copyright (c) 2018 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.

#include "LinePolygonsCrossings.h"

#include <algorithm>

#include "../utils/polygonUtils.h"
#include "../sliceDataStorage.h"
#include "../utils/SVG.h"

namespace cura {

LinePolygonsCrossings::Crossing::Crossing(const coord_t x, const size_t point_idx)
: x(x)
, point_idx(point_idx)
{
}

LinePolygonsCrossings::PolyCrossings::PolyCrossings(const size_t poly_idx)
: poly_idx(poly_idx)
, min(std::numeric_limits<coord_t>::max(), NO_INDEX), max(std::numeric_limits<coord_t>::min(), NO_INDEX)
, n_crossings(0)
{
}

bool LinePolygonsCrossings::calcScanlineCrossings(bool fail_on_unavoidable_obstacles)
{
    min_crossing_idx = NO_INDEX;
    max_crossing_idx = NO_INDEX;

    for(unsigned int poly_idx = 0; poly_idx < boundary.size(); poly_idx++)
    {
        PolyCrossings minMax(poly_idx);
        ConstPolygonRef poly = boundary[poly_idx];
        Point p0 = transformation_matrix.apply(poly[poly.size() - 1]);
        for(unsigned int point_idx = 0; point_idx < poly.size(); point_idx++)
        {
            Point p1 = transformation_matrix.apply(poly[point_idx]);
            if ((p0.Y >= transformed_startPoint.Y && p1.Y <= transformed_startPoint.Y) || (p1.Y >= transformed_startPoint.Y && p0.Y <= transformed_startPoint.Y))
            { // if line segment crosses the line through the transformed start and end point (aka scanline)
                if (p1.Y == p0.Y) //Line segment is parallel with the scanline. That means that both endpoints lie on the scanline, so they will have intersected with the adjacent line.
                {
                    p0 = p1;
                    continue;
                }
                const coord_t x = p0.X + (p1.X - p0.X) * (transformed_startPoint.Y - p0.Y) / (p1.Y - p0.Y); // intersection point between line segment and the scanline

                if (x >= transformed_startPoint.X && x <= transformed_endPoint.X)
                {
                    if (!((p1.Y == transformed_startPoint.Y && p1.Y < p0.Y) || (p0.Y == transformed_startPoint.Y && p0.Y < p1.Y)))
                    { // perform edge case only for line segments on and below the scanline, not for line segments on and above.
                        // \/ will be no crossings and /\ two, but most importantly | will be one crossing.
                        minMax.n_crossings++;
                    }
                    if(x < minMax.min.x) //For the leftmost intersection, move x left to stay outside of the border.
                                         //Note: The actual distance from the intersection to the border is almost always less than dist_to_move_boundary_point_outside, since it only moves along the direction of the scanline.
                    {
                        minMax.min.x = x;
                        minMax.min.point_idx = point_idx;
                    }
                    if(x > minMax.max.x) //For the rightmost intersection, move x right to stay outside of the border.
                    {
                        minMax.max.x = x;
                        minMax.max.point_idx = point_idx;
                    }
                }
            }
            p0 = p1;
        }

        if (fail_on_unavoidable_obstacles && minMax.n_crossings % 2 == 1)
        { // if start area and end area are not the same
            return false;
        }
        else if (minMax.min.point_idx != NO_INDEX) // then always also max.point_idx != NO_INDEX
        { // if this polygon crossed the scanline
            if (min_crossing_idx == NO_INDEX || minMax.min.x < crossings[min_crossing_idx].min.x) { min_crossing_idx = crossings.size(); }
            if (max_crossing_idx == NO_INDEX || minMax.max.x > crossings[max_crossing_idx].max.x) { max_crossing_idx = crossings.size(); }
            crossings.push_back(minMax);
        }
    }
    return true;
}

bool LinePolygonsCrossings::lineSegmentCollidesWithBoundary()
{
    Point diff = endPoint - startPoint;

    transformation_matrix = PointMatrix(diff);
    transformed_startPoint = transformation_matrix.apply(startPoint);
    transformed_endPoint = transformation_matrix.apply(endPoint);

    for(ConstPolygonRef poly : boundary)
    {
        Point p0 = transformation_matrix.apply(poly.back());
        for(Point p1_ : poly)
        {
            Point p1 = transformation_matrix.apply(p1_);
            // when the boundary just touches the line don't disambiguate between the boundary moving on to actually cross the line
            // and the boundary bouncing back, resulting in not a real collision - to keep the algorithm simple.
            //
            // disregard overlapping line segments; probably the next or previous line segment is not overlapping, but will give a collision
            // when the boundary line segment fully overlaps with the line segment this edge case is not viewed as a collision
            if (p1.Y != p0.Y && ((p0.Y >= transformed_startPoint.Y && p1.Y <= transformed_startPoint.Y) || (p1.Y >= transformed_startPoint.Y && p0.Y <= transformed_startPoint.Y)))
            {
                int64_t x = p0.X + (p1.X - p0.X) * (transformed_startPoint.Y - p0.Y) / (p1.Y - p0.Y);

                if (x > transformed_startPoint.X && x < transformed_endPoint.X)
                {
                    return true;
                }
            }
            p0 = p1;
        }
    }

    return false;
}

bool LinePolygonsCrossings::generateCombingPath(CombPath& combPath, int64_t max_comb_distance_ignored, bool fail_on_unavoidable_obstacles)
{
    if (shorterThen(endPoint - startPoint, max_comb_distance_ignored) || !lineSegmentCollidesWithBoundary())
    {
        //We're not crossing any boundaries. So skip the comb generation.
        combPath.push_back(startPoint);
        combPath.push_back(endPoint);
        return true;
    }

    bool success = calcScanlineCrossings(fail_on_unavoidable_obstacles);
    if (!success)
    {
        return false;
    }

    CombPath basicPath;
    generateBasicCombingPath(basicPath);
    optimizePath(basicPath, combPath);
//     combPath = basicPath; // uncomment to disable comb path optimization
    return true;
}

void LinePolygonsCrossings::generateBasicCombingPath(CombPath& combPath)
{
    for (PolyCrossings* crossing = getNextPolygonAlongScanline(transformed_startPoint.X)
        ; crossing != nullptr
        ; crossing = getNextPolygonAlongScanline(crossing->max.x))
    {
        generateBasicCombingPath(*crossing, combPath);
    }
    combPath.push_back(endPoint);
}

void LinePolygonsCrossings::generateBasicCombingPath(PolyCrossings& polyCrossings, CombPath& combPath)
{
    // minimise the path length by measuring the length of both paths around the polygon so we can determine the shorter path

    ConstPolygonRef poly = boundary[polyCrossings.poly_idx];
    combPath.push_back(transformation_matrix.unapply(Point(polyCrossings.min.x - std::abs(dist_to_move_boundary_point_outside), transformed_startPoint.Y)));

    // follow the path in the same direction as the winding order of the boundary polygon
    std::vector<Point> fwd_points;
    Point prev = combPath.back();
    coord_t fwd_len = 0;
    for (unsigned int point_idx = polyCrossings.min.point_idx
        ; point_idx != polyCrossings.max.point_idx
        ; point_idx = (point_idx < poly.size() - 1) ? (point_idx + 1) : (0))
    {
        const Point p = PolygonUtils::getBoundaryPointWithOffset(poly, point_idx, dist_to_move_boundary_point_outside);
        fwd_points.push_back(p);
        fwd_len += vSize(p - prev);
        prev = p;
    }

    const Point last = transformation_matrix.unapply(Point(polyCrossings.max.x + std::abs(dist_to_move_boundary_point_outside), transformed_startPoint.Y));

    if (fwd_points.size() > 0)
    {
        fwd_len += vSize(last - fwd_points.back());
    }

    // follow the path in the opposite direction of the winding order of the boundary polygon
    std::vector<Point> rev_points;
    prev = combPath.back();
    coord_t rev_len = 0;
    unsigned int min_idx = (polyCrossings.min.point_idx == 0)? poly.size() - 1 : polyCrossings.min.point_idx - 1;
    unsigned int max_idx = (polyCrossings.max.point_idx == 0)? poly.size() - 1 : polyCrossings.max.point_idx - 1;
    for (unsigned int point_idx = min_idx
        ; point_idx != max_idx
        ; point_idx = (point_idx > 0) ? (point_idx - 1) : (poly.size() - 1))
    {
        const Point p = PolygonUtils::getBoundaryPointWithOffset(poly, point_idx, dist_to_move_boundary_point_outside);
        rev_points.push_back(p);
        rev_len += vSize(p - prev);
        prev = p;
        if (rev_len > fwd_len)
        {
            // this path is already longer than the forward path so there's no point in carrying on
            break;
        }
    }

    if (rev_points.size() > 0)
    {
        rev_len += vSize(last - rev_points.back());
    }

    // use the points from the shortest path
    for (auto& p : (fwd_len < rev_len) ? fwd_points : rev_points)
    {
        combPath.push_back(p);
    }
    combPath.push_back(last);
}

LinePolygonsCrossings::PolyCrossings* LinePolygonsCrossings::getNextPolygonAlongScanline(int64_t x)
{
    PolyCrossings* ret = nullptr;
    for(PolyCrossings& crossing : crossings)
    {
        if (crossing.min.x > x && (ret == nullptr || crossing.min.x < ret->min.x) )
        {
            ret = &crossing;
        }
    }
    return ret;
}

bool LinePolygonsCrossings::optimizePath(CombPath& comb_path, CombPath& optimized_comb_path)
{
    optimized_comb_path.push_back(startPoint);
    for(unsigned int point_idx = 1; point_idx<comb_path.size(); point_idx++)
    {
        if(comb_path[point_idx] == comb_path[point_idx - 1]) //Two points are the same. Skip the second.
        {
            continue;
        }
        Point& current_point = optimized_comb_path.back();
        if (PolygonUtils::polygonCollidesWithLineSegment(current_point, comb_path[point_idx], loc_to_line_grid))
        {
            if (PolygonUtils::polygonCollidesWithLineSegment(current_point, comb_path[point_idx - 1], loc_to_line_grid))
            {
                comb_path.cross_boundary = true;
            }
            // before we add this point, can we discard some of the previous points and still avoid clashing with the combing boundary?
            const int max_short_circuit_len = 1 << 3; // test distances of 8, 4, 2, 1
            for (unsigned n = std::min(max_short_circuit_len, (int)optimized_comb_path.size()); n > 0; n >>= 1)
            {
                if (optimized_comb_path.size() > n && !PolygonUtils::polygonCollidesWithLineSegment(optimized_comb_path[optimized_comb_path.size() - n - 1], comb_path[point_idx - 1], loc_to_line_grid))
                {
                    // we can remove n points from the path without it clashing with the combing boundary
                    for (unsigned i = 0; i < n; ++i)
                    {
                        optimized_comb_path.pop_back();
                    }
                    break;
                }
            }
            optimized_comb_path.push_back(comb_path[point_idx - 1]);

            if (point_idx == 1)
            {
                // we have just inserted the first point in the path that touches the boundary
                // and as the first line in the comb is pointing from the start point towards the
                // end point, we can maybe save some travel by replacing this first point with another
                // that also lies on the second path of the comb, like this:
                //
                //         1
                //        /|
                //       / |
                //      /  |           ===>  ----1
                //     S   |                 S   |
                //         |                     |
                //         2----3 ...            2----3 ...
                //

                Point p = optimized_comb_path.back();
                for (float frac : { 0.9, 0.9, 0.7, 0.5 })
                {
                    // slide p towards the second point in the comb path
                    p = comb_path[1] + (p - comb_path[1]) * frac;
                    if (!PolygonUtils::polygonCollidesWithLineSegment(startPoint, p, loc_to_line_grid))
                    {
                        // using the new corner doesn't cause a conflict
                        optimized_comb_path.back() = p;
                    }
                    else
                    {
                        // quit loop and keep what we know to be good
                        break;
                    }
                }
            }
        }
        else
        {
            // : dont add the newest point

            // TODO: add the below extra optimization? (+/- 7% extra computation time, +/- 2% faster print for Dual_extrusion_support_generation.stl)
            while (optimized_comb_path.size() > 1)
            {
                if (PolygonUtils::polygonCollidesWithLineSegment(optimized_comb_path[optimized_comb_path.size() - 2], comb_path[point_idx], loc_to_line_grid))
                {
                    break;
                }
                else
                {
                    optimized_comb_path.pop_back();
                }
            }
        }
    }

    if (optimized_comb_path.size() > 1)
    {
        const unsigned n = optimized_comb_path.size();
        // the penultimate corner may be deleted if the resulting path doesn't conflict with the boundary
        if (!PolygonUtils::polygonCollidesWithLineSegment(optimized_comb_path[n - 2], comb_path.back(), loc_to_line_grid))
        {
            optimized_comb_path.pop_back();
        }
        else {
            // that wasn't possible so try and move the penultimate corner without conficting with the boundary
            // in exactly the same way as we did at the start of the path
            for (float frac : { 0.9, 0.9, 0.7, 0.5 })
            {
                // make a new point between the penultimate corner and the corner before that
                Point p = optimized_comb_path[n - 2] + (optimized_comb_path[n - 1] - optimized_comb_path[n - 2]) * frac;
                if (!PolygonUtils::polygonCollidesWithLineSegment(p, comb_path.back(), loc_to_line_grid))
                {
                    // using the new corner doesn't cause a conflict
                    optimized_comb_path[n - 1] = p;
                }
                else
                {
                    // quit loop and keep what we know to be good
                    break;
                }
            }
        }
    }

    optimized_comb_path.push_back(comb_path.back());
    return true;
}

}//namespace cura
<|endoftext|>


<commit_before>#include "tango-gl-renderer/axis.h"
#include "tango-gl-renderer/camera.h"
#include "tango-gl-renderer/cube.h"
#include "tango-gl-renderer/frustum.h"
#include "tango-gl-renderer/gl_util.h"
#include "tango-gl-renderer/grid.h"
#include "tango-gl-renderer/trace.h"

#include "dance_steps.h"
#include "tango_data.h"

const glm::vec3 kCubeScale = glm::vec3(0.3f, 0.3f, 0.3f);

DanceSteps::DanceSteps(){
	c = new Cube();
	c->SetScale(kCubeScale);
}

DanceSteps::~DanceSteps(){
	delete c;
}

void DanceSteps::Render(const glm::mat4& projection_mat, const glm::mat4& view_mat) const {
	float floor_height = TangoData::GetInstance().floor_height;
	for(float y = 0.0f; y < 10.0f; y += 1.0f){
		for(float x = 0.0f; x < 10.0f; x += 2.0f){
			c->SetPosition(glm::vec3(x,floor_height,y));
			c->Render(projection_mat, view_mat);
		}
	}
}
<commit_msg>First work on making dance floor<commit_after>#include "tango-gl-renderer/axis.h"
#include "tango-gl-renderer/camera.h"
#include "tango-gl-renderer/cube.h"
#include "tango-gl-renderer/frustum.h"
#include "tango-gl-renderer/gl_util.h"
#include "tango-gl-renderer/grid.h"
#include "tango-gl-renderer/trace.h"

#include "dance_steps.h"
#include "tango_data.h"

const glm::vec3 kCubeScale = glm::vec3(0.1f, 0.001f, 0.1f);

DanceSteps::DanceSteps(){
	c = new Cube();
	c->SetScale(kCubeScale);
}

DanceSteps::~DanceSteps(){
	delete c;
}

void DanceSteps::Render(const glm::mat4& projection_mat, const glm::mat4& view_mat) const {
	float floor_height = TangoData::GetInstance().floor_height;
	for(float y = 0.0f; y < 10.0f; y += 0.5f){
		for(float x = 0.0f; x < 10.0f; x += 0.5f){
			c->SetPosition(glm::vec3(x,floor_height,y));
			c->Render(projection_mat, view_mat);
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2015-2018 Elviss Strazdins. All rights reserved.

#pragma once

#include <vector>
#include "math/Box3.hpp"
#include "math/Plane.hpp"
#include "math/Vector4.hpp"

namespace ouzel
{
    class ConvexVolume
    {
    public:
        ConvexVolume()
        {
        }

        ConvexVolume(const std::vector<Plane>& initPlanes):
            planes(initPlanes)
        {
        }

        bool isPointInside(const Vector4& position)
        {
            for (const Plane& plane : planes)
            {
                if (plane.dot(position) < 0.0F)
                    return false;
            }

            return true;
        }

        bool isSphereInside(const Vector4& position, float radius)
        {
            for (const Plane& plane : planes)
            {
                if (plane.dot(position) < -radius)
                    return false;
            }

            return true;
        }

        bool isBoxInside(const Box3& box)
        {
            for (const Plane& plane : planes)
            {
                if (plane.dot(Vector4(box.min.x, box.min.y, box.min.z, 1.0F)) < 0.0F &&
                    plane.dot(Vector4(box.max.x, box.min.y, box.min.z, 1.0F)) < 0.0F &&
                    plane.dot(Vector4(box.min.x, box.max.y, box.min.z, 1.0F)) < 0.0F &&
                    plane.dot(Vector4(box.min.x, box.min.y, box.max.z, 1.0F)) < 0.0F &&
                    plane.dot(Vector4(box.max.x, box.max.y, box.min.z, 1.0F)) < 0.0F &&
                    plane.dot(Vector4(box.max.x, box.min.y, box.max.z, 1.0F)) < 0.0F &&
                    plane.dot(Vector4(box.min.x, box.max.y, box.max.z, 1.0F)) < 0.0F &&
                    plane.dot(Vector4(box.max.x, box.max.y, box.max.z, 1.0F)) < 0.0F)
                    return false;
            }

            return true;
        }

        std::vector<Plane> planes;
    };
}
<commit_msg>Optimize the isBoxInside method Make Plane's methods const<commit_after>// Copyright 2015-2018 Elviss Strazdins. All rights reserved.

#pragma once

#include <vector>
#include "math/Box3.hpp"
#include "math/Plane.hpp"
#include "math/Vector4.hpp"

namespace ouzel
{
    class ConvexVolume
    {
    public:
        ConvexVolume()
        {
        }

        ConvexVolume(const std::vector<Plane>& initPlanes):
            planes(initPlanes)
        {
        }

        bool isPointInside(const Vector4& position) const
        {
            for (const Plane& plane : planes)
            {
                if (plane.dot(position) < 0.0F)
                    return false;
            }

            return true;
        }

        bool isSphereInside(const Vector4& position, float radius) const
        {
            for (const Plane& plane : planes)
            {
                if (plane.dot(position) < -radius)
                    return false;
            }

            return true;
        }

        bool isBoxInside(const Box3& box) const
        {
            Vector4 leftBottomBack(box.min.x, box.min.y, box.min.z, 1.0F);
            Vector4 leftBottomFront(box.min.x, box.min.y, box.max.z, 1.0F);
            Vector4 leftTopBack(box.min.x, box.max.y, box.min.z, 1.0F);
            Vector4 leftTopFront(box.min.x, box.max.y, box.max.z, 1.0F);
            Vector4 rightBottomBack(box.max.x, box.min.y, box.min.z, 1.0F);
            Vector4 rightBottomFront(box.max.x, box.min.y, box.max.z, 1.0F);
            Vector4 rightTopBack(box.max.x, box.max.y, box.min.z, 1.0F);
            Vector4 rightTopFront(box.max.x, box.max.y, box.max.z, 1.0F);

            for (const Plane& plane : planes)
            {
                if (plane.dot(leftBottomBack) < 0.0F &&
                    plane.dot(leftBottomFront) < 0.0F &&
                    plane.dot(leftTopBack) < 0.0F &&
                    plane.dot(leftTopFront) < 0.0F &&
                    plane.dot(rightBottomBack) < 0.0F &&
                    plane.dot(rightBottomFront) < 0.0F &&
                    plane.dot(rightTopBack) < 0.0F &&
                    plane.dot(rightTopFront) < 0.0F)
                    return false;
            }

            return true;
        }

        std::vector<Plane> planes;
    };
}
<|endoftext|>


<commit_before>/*************************************************************************
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright 2000, 2010 Oracle and/or its affiliates.
*
* OpenOffice.org - a multi-platform office productivity suite
*
* This file is part of OpenOffice.org.
*
* OpenOffice.org is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenOffice.org is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenOffice.org.  If not, see
* <http://www.openoffice.org/license.html>
* for a copy of the LGPLv3 License.
*
************************************************************************/

#include "precompiled_configmgr.hxx"
#include "sal/config.h"

#include <cstddef>

#include "com/sun/star/uno/Reference.hxx"
#include "com/sun/star/uno/RuntimeException.hpp"
#include "com/sun/star/uno/XInterface.hpp"
#include "osl/diagnose.hxx"
#include "rtl/string.h"
#include "rtl/ustring.h"
#include "rtl/ustring.hxx"

#include "span.hxx"
#include "xcdparser.hxx"
#include "xcsparser.hxx"
#include "xcuparser.hxx"
#include "xmldata.hxx"
#include "xmlreader.hxx"

namespace configmgr {

namespace {

namespace css = com::sun::star;

}

XcdParser::XcdParser(int layer, Dependencies const & dependencies, Data * data):
    layer_(layer), dependencies_(dependencies), data_(data), state_(STATE_START)
{}

XcdParser::~XcdParser() {}

XmlReader::Text XcdParser::getTextMode() {
    return nestedParser_.is()
        ? nestedParser_->getTextMode() : XmlReader::TEXT_NONE;
}

bool XcdParser::startElement(
    XmlReader & reader, XmlReader::Namespace ns, Span const & name)
{
    if (nestedParser_.is()) {
        OSL_ASSERT(nesting_ != LONG_MAX);
        ++nesting_;
        return nestedParser_->startElement(reader, ns, name);
    }
    switch (state_) {
    case STATE_START:
        if (ns == XmlReader::NAMESPACE_OOR &&
            name.equals(RTL_CONSTASCII_STRINGPARAM("data")))
        {
            state_ = STATE_DEPENDENCIES;
            return true;
        }
        break;
    case STATE_DEPENDENCIES:
        if (ns == XmlReader::NAMESPACE_NONE &&
            name.equals(RTL_CONSTASCII_STRINGPARAM("dependency")))
        {
            if (dependency_.getLength() == 0) {
                Span attrFile;
                for (;;) {
                    XmlReader::Namespace attrNs;
                    Span attrLn;
                    if (!reader.nextAttribute(&attrNs, &attrLn)) {
                        break;
                    }
                    if (attrNs == XmlReader::NAMESPACE_NONE && //TODO: _OOR
                        attrLn.equals(RTL_CONSTASCII_STRINGPARAM("file")))
                    {
                        attrFile = reader.getAttributeValue(false);
                    }
                }
                if (!attrFile.is()) {
                    throw css::uno::RuntimeException(
                        (rtl::OUString(
                            RTL_CONSTASCII_USTRINGPARAM(
                                "no dependency file attribute in ")) +
                         reader.getUrl()),
                        css::uno::Reference< css::uno::XInterface >());
                }
                dependency_ = xmldata::convertFromUtf8(attrFile);
                if (dependency_.getLength() == 0) {
                    throw css::uno::RuntimeException(
                        (rtl::OUString(
                            RTL_CONSTASCII_USTRINGPARAM(
                                "bad dependency file attribute in ")) +
                         reader.getUrl()),
                        css::uno::Reference< css::uno::XInterface >());
                }
            }
            if (dependencies_.find(dependency_) == dependencies_.end()) {
                return false;
            }
            state_ = STATE_DEPENDENCY;
            dependency_ = rtl::OUString();
            return true;
        }
        state_ = STATE_COMPONENTS;
        // fall through
    case STATE_COMPONENTS:
        if (ns == XmlReader::NAMESPACE_OOR &&
            name.equals(RTL_CONSTASCII_STRINGPARAM("component-schema")))
        {
            nestedParser_ = new XcsParser(layer_, data_);
            nesting_ = 1;
            return nestedParser_->startElement(reader, ns, name);
        }
        if (ns == XmlReader::NAMESPACE_OOR &&
            name.equals(RTL_CONSTASCII_STRINGPARAM("component-data")))
        {
            nestedParser_ = new XcuParser(layer_ + 1, data_, 0);
            nesting_ = 1;
            return nestedParser_->startElement(reader, ns, name);
        }
        break;
    default: // STATE_DEPENDENCY
        OSL_ASSERT(false); // this cannot happen
        break;
    }
    throw css::uno::RuntimeException(
        (rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("bad member <")) +
         xmldata::convertFromUtf8(name) +
         rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("> in ")) + reader.getUrl()),
        css::uno::Reference< css::uno::XInterface >());
}

void XcdParser::endElement(XmlReader const & reader) {
    if (nestedParser_.is()) {
        nestedParser_->endElement(reader);
        if (--nesting_ == 0) {
            nestedParser_.clear();
        }
    } else {
        switch (state_) {
        case STATE_DEPENDENCY:
            state_ = STATE_DEPENDENCIES;
            break;
        case STATE_DEPENDENCIES:
        case STATE_COMPONENTS:
            break;
        default:
            OSL_ASSERT(false); // this cannot happen
            break;
        }
    }
}

void XcdParser::characters(Span const & text) {
    if (nestedParser_.is()) {
        nestedParser_->characters(text);
    }
}

}
<commit_msg>#i109991#<commit_after>/*************************************************************************
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright 2000, 2010 Oracle and/or its affiliates.
*
* OpenOffice.org - a multi-platform office productivity suite
*
* This file is part of OpenOffice.org.
*
* OpenOffice.org is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenOffice.org is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenOffice.org.  If not, see
* <http://www.openoffice.org/license.html>
* for a copy of the LGPLv3 License.
*
************************************************************************/

#include "precompiled_configmgr.hxx"
#include "sal/config.h"

#include <climits>

#include "com/sun/star/uno/Reference.hxx"
#include "com/sun/star/uno/RuntimeException.hpp"
#include "com/sun/star/uno/XInterface.hpp"
#include "osl/diagnose.hxx"
#include "rtl/string.h"
#include "rtl/ustring.h"
#include "rtl/ustring.hxx"

#include "span.hxx"
#include "xcdparser.hxx"
#include "xcsparser.hxx"
#include "xcuparser.hxx"
#include "xmldata.hxx"
#include "xmlreader.hxx"

namespace configmgr {

namespace {

namespace css = com::sun::star;

}

XcdParser::XcdParser(int layer, Dependencies const & dependencies, Data * data):
    layer_(layer), dependencies_(dependencies), data_(data), state_(STATE_START)
{}

XcdParser::~XcdParser() {}

XmlReader::Text XcdParser::getTextMode() {
    return nestedParser_.is()
        ? nestedParser_->getTextMode() : XmlReader::TEXT_NONE;
}

bool XcdParser::startElement(
    XmlReader & reader, XmlReader::Namespace ns, Span const & name)
{
    if (nestedParser_.is()) {
        OSL_ASSERT(nesting_ != LONG_MAX);
        ++nesting_;
        return nestedParser_->startElement(reader, ns, name);
    }
    switch (state_) {
    case STATE_START:
        if (ns == XmlReader::NAMESPACE_OOR &&
            name.equals(RTL_CONSTASCII_STRINGPARAM("data")))
        {
            state_ = STATE_DEPENDENCIES;
            return true;
        }
        break;
    case STATE_DEPENDENCIES:
        if (ns == XmlReader::NAMESPACE_NONE &&
            name.equals(RTL_CONSTASCII_STRINGPARAM("dependency")))
        {
            if (dependency_.getLength() == 0) {
                Span attrFile;
                for (;;) {
                    XmlReader::Namespace attrNs;
                    Span attrLn;
                    if (!reader.nextAttribute(&attrNs, &attrLn)) {
                        break;
                    }
                    if (attrNs == XmlReader::NAMESPACE_NONE && //TODO: _OOR
                        attrLn.equals(RTL_CONSTASCII_STRINGPARAM("file")))
                    {
                        attrFile = reader.getAttributeValue(false);
                    }
                }
                if (!attrFile.is()) {
                    throw css::uno::RuntimeException(
                        (rtl::OUString(
                            RTL_CONSTASCII_USTRINGPARAM(
                                "no dependency file attribute in ")) +
                         reader.getUrl()),
                        css::uno::Reference< css::uno::XInterface >());
                }
                dependency_ = xmldata::convertFromUtf8(attrFile);
                if (dependency_.getLength() == 0) {
                    throw css::uno::RuntimeException(
                        (rtl::OUString(
                            RTL_CONSTASCII_USTRINGPARAM(
                                "bad dependency file attribute in ")) +
                         reader.getUrl()),
                        css::uno::Reference< css::uno::XInterface >());
                }
            }
            if (dependencies_.find(dependency_) == dependencies_.end()) {
                return false;
            }
            state_ = STATE_DEPENDENCY;
            dependency_ = rtl::OUString();
            return true;
        }
        state_ = STATE_COMPONENTS;
        // fall through
    case STATE_COMPONENTS:
        if (ns == XmlReader::NAMESPACE_OOR &&
            name.equals(RTL_CONSTASCII_STRINGPARAM("component-schema")))
        {
            nestedParser_ = new XcsParser(layer_, data_);
            nesting_ = 1;
            return nestedParser_->startElement(reader, ns, name);
        }
        if (ns == XmlReader::NAMESPACE_OOR &&
            name.equals(RTL_CONSTASCII_STRINGPARAM("component-data")))
        {
            nestedParser_ = new XcuParser(layer_ + 1, data_, 0);
            nesting_ = 1;
            return nestedParser_->startElement(reader, ns, name);
        }
        break;
    default: // STATE_DEPENDENCY
        OSL_ASSERT(false); // this cannot happen
        break;
    }
    throw css::uno::RuntimeException(
        (rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("bad member <")) +
         xmldata::convertFromUtf8(name) +
         rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("> in ")) + reader.getUrl()),
        css::uno::Reference< css::uno::XInterface >());
}

void XcdParser::endElement(XmlReader const & reader) {
    if (nestedParser_.is()) {
        nestedParser_->endElement(reader);
        if (--nesting_ == 0) {
            nestedParser_.clear();
        }
    } else {
        switch (state_) {
        case STATE_DEPENDENCY:
            state_ = STATE_DEPENDENCIES;
            break;
        case STATE_DEPENDENCIES:
        case STATE_COMPONENTS:
            break;
        default:
            OSL_ASSERT(false); // this cannot happen
            break;
        }
    }
}

void XcdParser::characters(Span const & text) {
    if (nestedParser_.is()) {
        nestedParser_->characters(text);
    }
}

}
<|endoftext|>