<commit_before>//===- SourceCoverageViewHTML.cpp - A html code coverage view -------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file This file implements the html coverage renderer.
///
//===----------------------------------------------------------------------===//

#include "SourceCoverageViewHTML.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Path.h"

using namespace llvm;

namespace {

// Return a string with the special characters in \p Str escaped.
std::string escape(StringRef Str) {
  std::string Result;
  for (char C : Str) {
    if (C == '&')
      Result += "&";
    else if (C == '<')
      Result += "<";
    else if (C == '>')
      Result += ">";
    else if (C == '\"')
      Result += """;
    else
      Result += C;
  }
  return Result;
}

// Create a \p Name tag around \p Str, and optionally set its \p ClassName.
std::string tag(const std::string &Name, const std::string &Str,
                const std::string &ClassName = "") {
  std::string Tag = "<" + Name;
  if (ClassName != "")
    Tag += " class='" + ClassName + "'";
  return Tag + ">" + Str + "</" + Name + ">";
}

// Create an anchor to \p Link with the label \p Str.
std::string a(const std::string &Link, const std::string &Str,
              const std::string &TargetType = "href") {
  return "" + Str + "";
}

const char *BeginHeader =
  ""
    "<meta name='viewport' content='width=device-width,initial-scale=1'>"
    "<meta charset='UTF-8'>";

const char *CSSForCoverage =
    R"(.red {
  background-color: #FFD0D0;
}
.cyan {
  background-color: cyan;
}
body {
  font-family: -apple-system, sans-serif;
}
pre {
  margin-top: 0px !important;
  margin-bottom: 0px !important;
}
.source-name-title {
  padding: 5px 10px;
  border-bottom: 1px solid #dbdbdb;
  background-color: #eee;
}
.centered {
  display: table;
  margin-left: auto;
  margin-right: auto;
  border: 1px solid #dbdbdb;
  border-radius: 3px;
}
.expansion-view {
  background-color: rgba(0, 0, 0, 0);
  margin-left: 0px;
  margin-top: 5px;
  margin-right: 5px;
  margin-bottom: 5px;
  border: 1px solid #dbdbdb;
  border-radius: 3px;
}
table {
  border-collapse: collapse;
}
.line-number {
  text-align: right;
  color: #aaa;
}
.covered-line {
  text-align: right;
  color: #0080ff;
}
.uncovered-line {
  text-align: right;
  color: #ff3300;
}
.tooltip {
  position: relative;
  display: inline;
  background-color: #b3e6ff;
  text-decoration: none;
}
.tooltip span.tooltip-content {
  position: absolute;
  width: 100px;
  margin-left: -50px;
  color: #FFFFFF;
  background: #000000;
  height: 30px;
  line-height: 30px;
  text-align: center;
  visibility: hidden;
  border-radius: 6px;
}
.tooltip span.tooltip-content:after {
  content: '';
  position: absolute;
  top: 100%;
  left: 50%;
  margin-left: -8px;
  width: 0; height: 0;
  border-top: 8px solid #000000;
  border-right: 8px solid transparent;
  border-left: 8px solid transparent;
}
:hover.tooltip span.tooltip-content {
  visibility: visible;
  opacity: 0.8;
  bottom: 30px;
  left: 50%;
  z-index: 999;
}
th, td {
  vertical-align: top;
  padding: 2px 5px;
  border-collapse: collapse;
  border-right: solid 1px #eee;
  border-left: solid 1px #eee;
}
td:first-child {
  border-left: none;
}
td:last-child {
  border-right: none;
}
)";

const char *EndHeader = "";

const char *BeginCenteredDiv = "
";

const char *EndCenteredDiv = "
";

const char *BeginSourceNameDiv = "
";

const char *EndSourceNameDiv = "
";

const char *BeginCodeTD = "";

const char *EndCodeTD = "	";

const char *BeginPre = "
";

const char *EndPre = "
";

const char *BeginExpansionDiv = "
";

const char *EndExpansionDiv = "
";

const char *BeginTable = "
";

const char *EndTable = "
";

std::string getPathToStyle(StringRef ViewPath) {
  std::string PathToStyle = "";
  std::string PathSep = sys::path::get_separator();
  unsigned NumSeps = ViewPath.count(PathSep);
  for (unsigned I = 0, E = NumSeps; I < E; ++I)
    PathToStyle += ".." + PathSep;
  return PathToStyle + "style.css";
}

void emitPrelude(raw_ostream &OS, const std::string &PathToStyle = "") {
  OS << "<!doctype html>"
        ""
     << BeginHeader;

  // Link to a stylesheet if one is available. Otherwise, use the default style.
  if (PathToStyle.empty())
    OS << "";
  else
    OS << "<link rel='stylesheet' type='text/css' href='" << escape(PathToStyle)
       << "'>";

  OS << EndHeader << "" << BeginCenteredDiv;
}

void emitEpilog(raw_ostream &OS) {
  OS << EndCenteredDiv << ""
                          "";
}

} // anonymous namespace

Expected<CoveragePrinter::OwnedStream>
CoveragePrinterHTML::createViewFile(StringRef Path, bool InToplevel) {
  auto OSOrErr = createOutputStream(Path, "html", InToplevel);
  if (!OSOrErr)
    return OSOrErr;

  OwnedStream OS = std::move(OSOrErr.get());

  if (!Opts.hasOutputDirectory()) {
    emitPrelude(*OS.get());
  } else {
    std::string ViewPath = getOutputPath(Path, "html", InToplevel);
    emitPrelude(*OS.get(), getPathToStyle(ViewPath));
  }

  return std::move(OS);
}

void CoveragePrinterHTML::closeViewFile(OwnedStream OS) {
  emitEpilog(*OS.get());
}

Error CoveragePrinterHTML::createIndexFile(ArrayRef<StringRef> SourceFiles) {
  auto OSOrErr = createOutputStream("index", "html", /*InToplevel=*/true);
  if (Error E = OSOrErr.takeError())
    return E;
  auto OS = std::move(OSOrErr.get());
  raw_ostream &OSRef = *OS.get();

  // Emit a table containing links to reports for each file in the covmapping.
  assert(Opts.hasOutputDirectory() && "No output directory for index file");
  emitPrelude(OSRef, getPathToStyle(""));
  OSRef << BeginSourceNameDiv << "Index" << EndSourceNameDiv;
  OSRef << BeginTable;
  for (StringRef SF : SourceFiles) {
    std::string LinkText = escape(sys::path::relative_path(SF));
    std::string LinkTarget =
        escape(getOutputPath(SF, "html", /*InToplevel=*/false));
    OSRef << tag("tr", tag("td", tag("pre", a(LinkTarget, LinkText), "code")));
  }
  OSRef << EndTable;
  emitEpilog(OSRef);

  // Emit the default stylesheet.
  auto CSSOrErr = createOutputStream("style", "css", /*InToplevel=*/true);
  if (Error E = CSSOrErr.takeError())
    return E;

  OwnedStream CSS = std::move(CSSOrErr.get());
  CSS->operator<<(CSSForCoverage);

  return Error::success();
}

void SourceCoverageViewHTML::renderViewHeader(raw_ostream &OS) {
  OS << BeginTable;
}

void SourceCoverageViewHTML::renderViewFooter(raw_ostream &OS) {
  OS << EndTable;
}

void SourceCoverageViewHTML::renderSourceName(raw_ostream &OS) {
  OS << BeginSourceNameDiv << tag("pre", escape(getSourceName()))
     << EndSourceNameDiv;
}

void SourceCoverageViewHTML::renderLinePrefix(raw_ostream &OS, unsigned) {
  OS << "
";
}

void SourceCoverageViewHTML::renderLineSuffix(raw_ostream &OS, unsigned) {
  // If this view has sub-views, renderLine() cannot close the view's cell.
  // Take care of it here, after all sub-views have been rendered.
  if (hasSubViews())
    OS << EndCodeTD;
  OS << "
";
}

void SourceCoverageViewHTML::renderViewDivider(raw_ostream &, unsigned) {
  // The table-based output makes view dividers unnecessary.
}

void SourceCoverageViewHTML::renderLine(
    raw_ostream &OS, LineRef L, const coverage::CoverageSegment *WrappedSegment,
    CoverageSegmentArray Segments, unsigned ExpansionCol, unsigned) {
  StringRef Line = L.Line;

  // Steps for handling text-escaping, highlighting, and tooltip creation:
  //
  // 1. Split the line into N+1 snippets, where N = |Segments|. The first
  //    snippet starts from Col=1 and ends at the start of the first segment.
  //    The last snippet starts at the last mapped column in the line and ends
  //    at the end of the line. Both are required but may be empty.

  SmallVector<std::string, 8> Snippets;

  unsigned LCol = 1;
  auto Snip = [&](unsigned Start, unsigned Len) {
    assert(Start + Len <= Line.size() && "Snippet extends past the EOL");
    Snippets.push_back(Line.substr(Start, Len));
    LCol += Len;
  };

  Snip(LCol - 1, Segments.empty() ? 0 : (Segments.front()->Col - 1));

  for (unsigned I = 1, E = Segments.size(); I < E; ++I) {
    assert(LCol == Segments[I - 1]->Col && "Snippet start position is wrong");
    Snip(LCol - 1, Segments[I]->Col - LCol);
  }

  // |Line| + 1 is needed to avoid underflow when, e.g |Line| = 0 and LCol = 1.
  Snip(LCol - 1, Line.size() + 1 - LCol);
  assert(LCol == Line.size() + 1 && "Final snippet doesn't reach the EOL");

  // 2. Escape all of the snippets.

  for (unsigned I = 0, E = Snippets.size(); I < E; ++I)
    Snippets[I] = escape(Snippets[I]);

  // 3. Use \p WrappedSegment to set the highlight for snippet 0. Use segment
  //    1 to set the highlight for snippet 2, segment 2 to set the highlight for
  //    snippet 3, and so on.

  Optional<std::string> Color;
  auto Highlight = [&](const std::string &Snippet) {
    return tag("span", Snippet, Color.getValue());
  };

  auto CheckIfUncovered = [](const coverage::CoverageSegment *S) {
    return !S || (S->HasCount && S->Count == 0);
  };

  if (CheckIfUncovered(WrappedSegment) ||
      CheckIfUncovered(Segments.empty() ? nullptr : Segments.front())) {
    Color = "red";
    Snippets[0] = Highlight(Snippets[0]);
  }

  for (unsigned I = 0, E = Segments.size(); I < E; ++I) {
    const auto *CurSeg = Segments[I];
    if (CurSeg->Col == ExpansionCol)
      Color = "cyan";
    else if (CheckIfUncovered(CurSeg))
      Color = "red";
    else
      Color = None;

    if (Color.hasValue())
      Snippets[I + 1] = Highlight(Snippets[I + 1]);
  }

  if (Color.hasValue() && Segments.empty())
    Snippets.back() = Highlight(Snippets.back());

  // 4. Snippets[1:N+1] correspond to \p Segments[0:N]: use these to generate
  //    sub-line region count tooltips if needed.

  bool HasMultipleRegions = [&] {
    unsigned RegionCount = 0;
    for (const auto *S : Segments)
      if (S->HasCount && S->IsRegionEntry)
        if (++RegionCount > 1)
          return true;
    return false;
  }();

  if (shouldRenderRegionMarkers(HasMultipleRegions)) {
    for (unsigned I = 0, E = Segments.size(); I < E; ++I) {
      const auto *CurSeg = Segments[I];
      if (!CurSeg->IsRegionEntry || !CurSeg->HasCount)
        continue;

      Snippets[I + 1] =
          tag("div", Snippets[I + 1] + tag("span", formatCount(CurSeg->Count),
                                           "tooltip-content"),
              "tooltip");
    }
  }

  OS << BeginCodeTD;
  OS << BeginPre;
  for (const auto &Snippet : Snippets)
    OS << Snippet;
  OS << EndPre;

  // If there are no sub-views left to attach to this cell, end the cell.
  // Otherwise, end it after the sub-views are rendered (renderLineSuffix()).
  if (!hasSubViews())
    OS << EndCodeTD;
}

void SourceCoverageViewHTML::renderLineCoverageColumn(
    raw_ostream &OS, const LineCoverageStats &Line) {
  std::string Count = "";
  if (Line.isMapped())
    Count = tag("pre", formatCount(Line.ExecutionCount));
  std::string CoverageClass =
      (Line.ExecutionCount > 0) ? "covered-line" : "uncovered-line";
  OS << tag("td", Count, CoverageClass);
}

void SourceCoverageViewHTML::renderLineNumberColumn(raw_ostream &OS,
                                                    unsigned LineNo) {
  std::string LineNoStr = utostr(uint64_t(LineNo));
  OS << tag("td", a("L" + LineNoStr, tag("pre", LineNoStr), "name"),
            "line-number");
}

void SourceCoverageViewHTML::renderRegionMarkers(raw_ostream &,
                                                 CoverageSegmentArray,
                                                 unsigned) {
  // Region markers are rendered in-line using tooltips.
}

void SourceCoverageViewHTML::renderExpansionSite(
    raw_ostream &OS, LineRef L, const coverage::CoverageSegment *WrappedSegment,
    CoverageSegmentArray Segments, unsigned ExpansionCol, unsigned ViewDepth) {
  // Render the line containing the expansion site. No extra formatting needed.
  renderLine(OS, L, WrappedSegment, Segments, ExpansionCol, ViewDepth);
}

void SourceCoverageViewHTML::renderExpansionView(raw_ostream &OS,
                                                 ExpansionView &ESV,
                                                 unsigned ViewDepth) {
  OS << BeginExpansionDiv;
  ESV.View->print(OS, /*WholeFile=*/false, /*ShowSourceName=*/false,
                  ViewDepth + 1);
  OS << EndExpansionDiv;
}

void SourceCoverageViewHTML::renderInstantiationView(raw_ostream &OS,
                                                     InstantiationView &ISV,
                                                     unsigned ViewDepth) {
  OS << BeginExpansionDiv;
  ISV.View->print(OS, /*WholeFile=*/false, /*ShowSourceName=*/true, ViewDepth);
  OS << EndExpansionDiv;
}
<commit_msg>Revert "[llvm-cov] Minor aesthetic improvements for html reports"<commit_after>//===- SourceCoverageViewHTML.cpp - A html code coverage view -------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file This file implements the html coverage renderer.
///
//===----------------------------------------------------------------------===//

#include "SourceCoverageViewHTML.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Path.h"

using namespace llvm;

namespace {

// Return a string with the special characters in \p Str escaped.
std::string escape(StringRef Str) {
  std::string Result;
  for (char C : Str) {
    if (C == '&')
      Result += "&";
    else if (C == '<')
      Result += "<";
    else if (C == '>')
      Result += ">";
    else if (C == '\"')
      Result += """;
    else
      Result += C;
  }
  return Result;
}

// Create a \p Name tag around \p Str, and optionally set its \p ClassName.
std::string tag(const std::string &Name, const std::string &Str,
                const std::string &ClassName = "") {
  std::string Tag = "<" + Name;
  if (ClassName != "")
    Tag += " class='" + ClassName + "'";
  return Tag + ">" + Str + "</" + Name + ">";
}

// Create an anchor to \p Link with the label \p Str.
std::string a(const std::string &Link, const std::string &Str,
              const std::string &TargetType = "href") {
  return "" + Str + "";
}

const char *BeginHeader =
  ""
    "<meta name='viewport' content='width=device-width,initial-scale=1'>"
    "<meta charset='UTF-8'>";

const char *CSSForCoverage =
    R"(.red {
  background-color: #FFD0D0;
}
.cyan {
  background-color: cyan;
}
body {
  font-family: -apple-system, sans-serif;
}
pre {
  margin-top: 0px !important;
  margin-bottom: 0px !important;
}
.source-name-title {
  padding: 5px 10px;
  border-bottom: 1px solid #dbdbdb;
  background-color: #eee;
}
.centered {
  display: table;
  margin-left: auto;
  margin-right: auto;
  border: 1px solid #dbdbdb;
  border-radius: 3px;
}
.expansion-view {
  background-color: rgba(0, 0, 0, 0);
  margin-left: 0px;
  margin-top: 5px;
  margin-right: 5px;
  margin-bottom: 5px;
  border: 1px solid #dbdbdb;
  border-radius: 3px;
}
table {
  border-collapse: collapse;
}
.line-number {
  text-align: right;
  color: #aaa;
}
.covered-line {
  text-align: right;
  color: #0080ff;
}
.uncovered-line {
  text-align: right;
  color: #ff3300;
}
.tooltip {
  position: relative;
  display: inline;
  background-color: #b3e6ff;
  text-decoration: none;
}
.tooltip span.tooltip-content {
  position: absolute;
  width: 100px;
  margin-left: -50px;
  color: #FFFFFF;
  background: #000000;
  height: 30px;
  line-height: 30px;
  text-align: center;
  visibility: hidden;
  border-radius: 6px;
}
.tooltip span.tooltip-content:after {
  content: '';
  position: absolute;
  top: 100%;
  left: 50%;
  margin-left: -8px;
  width: 0; height: 0;
  border-top: 8px solid #000000;
  border-right: 8px solid transparent;
  border-left: 8px solid transparent;
}
:hover.tooltip span.tooltip-content {
  visibility: visible;
  opacity: 0.8;
  bottom: 30px;
  left: 50%;
  z-index: 999;
}
th, td {
  vertical-align: top;
  padding: 2px 5px;
  border-collapse: collapse;
  border-right: solid 1px #eee;
  border-left: solid 1px #eee;
}
td:first-child {
  border-left: none;
}
td:last-child {
  border-right: none;
}
)";

const char *EndHeader = "";

const char *BeginCenteredDiv = "
";

const char *EndCenteredDiv = "
";

const char *BeginSourceNameDiv = "
";

const char *EndSourceNameDiv = "
";

const char *BeginCodeTD = "";

const char *EndCodeTD = "	";

const char *BeginPre = "
";

const char *EndPre = "
";

const char *BeginExpansionDiv = "
";

const char *EndExpansionDiv = "
";

const char *BeginTable = "
";

const char *EndTable = "
";

std::string getPathToStyle(StringRef ViewPath) {
  std::string PathToStyle = "";
  std::string PathSep = sys::path::get_separator();
  unsigned NumSeps = ViewPath.count(PathSep);
  for (unsigned I = 0, E = NumSeps; I < E; ++I)
    PathToStyle += ".." + PathSep;
  return PathToStyle + "style.css";
}

void emitPrelude(raw_ostream &OS, const std::string &PathToStyle = "") {
  OS << "<!doctype html>"
        ""
     << BeginHeader;

  // Link to a stylesheet if one is available. Otherwise, use the default style.
  if (PathToStyle.empty())
    OS << "";
  else
    OS << "<link rel='stylesheet' type='text/css' href='" << escape(PathToStyle)
       << "'>";

  OS << EndHeader << "" << BeginCenteredDiv;
}

void emitEpilog(raw_ostream &OS) {
  OS << EndCenteredDiv << ""
                          "";
}

} // anonymous namespace

Expected<CoveragePrinter::OwnedStream>
CoveragePrinterHTML::createViewFile(StringRef Path, bool InToplevel) {
  auto OSOrErr = createOutputStream(Path, "html", InToplevel);
  if (!OSOrErr)
    return OSOrErr;

  OwnedStream OS = std::move(OSOrErr.get());

  if (!Opts.hasOutputDirectory()) {
    emitPrelude(*OS.get());
  } else {
    std::string ViewPath = getOutputPath(Path, "html", InToplevel);
    emitPrelude(*OS.get(), getPathToStyle(ViewPath));
  }

  return std::move(OS);
}

void CoveragePrinterHTML::closeViewFile(OwnedStream OS) {
  emitEpilog(*OS.get());
}

Error CoveragePrinterHTML::createIndexFile(ArrayRef<StringRef> SourceFiles) {
  auto OSOrErr = createOutputStream("index", "html", /*InToplevel=*/true);
  if (Error E = OSOrErr.takeError())
    return E;
  auto OS = std::move(OSOrErr.get());
  raw_ostream &OSRef = *OS.get();

  // Emit a table containing links to reports for each file in the covmapping.
  assert(Opts.hasOutputDirectory() && "No output directory for index file");
  emitPrelude(OSRef, getPathToStyle(""));
  OSRef << BeginSourceNameDiv << "Index" << EndSourceNameDiv;
  OSRef << BeginTable;
  for (StringRef SF : SourceFiles) {
    std::string LinkText = escape(sys::path::relative_path(SF));
    std::string LinkTarget =
        escape(getOutputPath(SF, "html", /*InToplevel=*/false));
    OSRef << tag("tr", tag("td", tag("pre", a(LinkTarget, LinkText), "code")));
  }
  OSRef << EndTable;
  emitEpilog(OSRef);

  // Emit the default stylesheet.
  auto CSSOrErr = createOutputStream("style", "css", /*InToplevel=*/true);
  if (Error E = CSSOrErr.takeError())
    return E;

  OwnedStream CSS = std::move(CSSOrErr.get());
  CSS->operator<<(CSSForCoverage);

  return Error::success();
}

void SourceCoverageViewHTML::renderViewHeader(raw_ostream &OS) {
  OS << BeginTable;
}

void SourceCoverageViewHTML::renderViewFooter(raw_ostream &OS) {
  OS << EndTable;
}

void SourceCoverageViewHTML::renderSourceName(raw_ostream &OS) {
  OS << BeginSourceNameDiv << tag("pre", escape(getSourceName()))
     << EndSourceNameDiv;
}

void SourceCoverageViewHTML::renderLinePrefix(raw_ostream &OS, unsigned) {
  OS << "
";
}

void SourceCoverageViewHTML::renderLineSuffix(raw_ostream &OS, unsigned) {
  // If this view has sub-views, renderLine() cannot close the view's cell.
  // Take care of it here, after all sub-views have been rendered.
  if (hasSubViews())
    OS << EndCodeTD;
  OS << "
";
}

void SourceCoverageViewHTML::renderViewDivider(raw_ostream &, unsigned) {
  // The table-based output makes view dividers unnecessary.
}

void SourceCoverageViewHTML::renderLine(
    raw_ostream &OS, LineRef L, const coverage::CoverageSegment *WrappedSegment,
    CoverageSegmentArray Segments, unsigned ExpansionCol, unsigned) {
  StringRef Line = L.Line;

  // Steps for handling text-escaping, highlighting, and tooltip creation:
  //
  // 1. Split the line into N+1 snippets, where N = |Segments|. The first
  //    snippet starts from Col=1 and ends at the start of the first segment.
  //    The last snippet starts at the last mapped column in the line and ends
  //    at the end of the line. Both are required but may be empty.

  SmallVector<std::string, 8> Snippets;

  unsigned LCol = 1;
  auto Snip = [&](unsigned Start, unsigned Len) {
    assert(Start + Len <= Line.size() && "Snippet extends past the EOL");
    Snippets.push_back(Line.substr(Start, Len));
    LCol += Len;
  };

  Snip(LCol - 1, Segments.empty() ? 0 : (Segments.front()->Col - 1));

  for (unsigned I = 1, E = Segments.size(); I < E; ++I) {
    assert(LCol == Segments[I - 1]->Col && "Snippet start position is wrong");
    Snip(LCol - 1, Segments[I]->Col - LCol);
  }

  // |Line| + 1 is needed to avoid underflow when, e.g |Line| = 0 and LCol = 1.
  Snip(LCol - 1, Line.size() + 1 - LCol);
  assert(LCol == Line.size() + 1 && "Final snippet doesn't reach the EOL");

  // 2. Escape all of the snippets.

  for (unsigned I = 0, E = Snippets.size(); I < E; ++I)
    Snippets[I] = escape(Snippets[I]);

  // 3. Use \p WrappedSegment to set the highlight for snippets 0 and 1. Use
  //    segment 1 to set the highlight for snippet 2, segment 2 to set the
  //    highlight for snippet 3, and so on.

  Optional<std::string> Color;
  auto Highlight = [&](const std::string &Snippet) {
    return tag("span", Snippet, Color.getValue());
  };

  auto CheckIfUncovered = [](const coverage::CoverageSegment *S) {
    return S && S->HasCount && S->Count == 0;
  };

  if (CheckIfUncovered(WrappedSegment) ||
      CheckIfUncovered(Segments.empty() ? nullptr : Segments.front())) {
    Color = "red";
    Snippets[0] = Highlight(Snippets[0]);
    Snippets[1] = Highlight(Snippets[1]);
  }

  for (unsigned I = 1, E = Segments.size(); I < E; ++I) {
    const auto *CurSeg = Segments[I];
    if (CurSeg->Col == ExpansionCol)
      Color = "cyan";
    else if (CheckIfUncovered(CurSeg))
      Color = "red";
    else
      Color = None;

    if (Color.hasValue())
      Snippets[I + 1] = Highlight(Snippets[I + 1]);
  }

  // 4. Snippets[1:N+1] correspond to \p Segments[0:N]: use these to generate
  //    sub-line region count tooltips if needed.

  bool HasMultipleRegions = [&] {
    unsigned RegionCount = 0;
    for (const auto *S : Segments)
      if (S->HasCount && S->IsRegionEntry)
        if (++RegionCount > 1)
          return true;
    return false;
  }();

  if (shouldRenderRegionMarkers(HasMultipleRegions)) {
    for (unsigned I = 0, E = Segments.size(); I < E; ++I) {
      const auto *CurSeg = Segments[I];
      if (!CurSeg->IsRegionEntry || !CurSeg->HasCount)
        continue;

      Snippets[I + 1] =
          tag("div", Snippets[I + 1] + tag("span", formatCount(CurSeg->Count),
                                          "tooltip-content"),
              "tooltip");
    }
  }

  OS << BeginCodeTD;
  OS << BeginPre;
  for (const auto &Snippet : Snippets)
    OS << Snippet;
  OS << EndPre;

  // If there are no sub-views left to attach to this cell, end the cell.
  // Otherwise, end it after the sub-views are rendered (renderLineSuffix()).
  if (!hasSubViews())
    OS << EndCodeTD;
}

void SourceCoverageViewHTML::renderLineCoverageColumn(
    raw_ostream &OS, const LineCoverageStats &Line) {
  std::string Count = "";
  if (Line.isMapped())
    Count = tag("pre", formatCount(Line.ExecutionCount));
  std::string CoverageClass =
      (Line.ExecutionCount > 0) ? "covered-line" : "uncovered-line";
  OS << tag("td", Count, CoverageClass);
}

void SourceCoverageViewHTML::renderLineNumberColumn(raw_ostream &OS,
                                                    unsigned LineNo) {
  std::string LineNoStr = utostr(uint64_t(LineNo));
  OS << tag("td", a("L" + LineNoStr, tag("pre", LineNoStr), "name"),
            "line-number");
}

void SourceCoverageViewHTML::renderRegionMarkers(raw_ostream &,
                                                 CoverageSegmentArray,
                                                 unsigned) {
  // Region markers are rendered in-line using tooltips.
}

void SourceCoverageViewHTML::renderExpansionSite(
    raw_ostream &OS, LineRef L, const coverage::CoverageSegment *WrappedSegment,
    CoverageSegmentArray Segments, unsigned ExpansionCol, unsigned ViewDepth) {
  // Render the line containing the expansion site. No extra formatting needed.
  renderLine(OS, L, WrappedSegment, Segments, ExpansionCol, ViewDepth);
}

void SourceCoverageViewHTML::renderExpansionView(raw_ostream &OS,
                                                 ExpansionView &ESV,
                                                 unsigned ViewDepth) {
  OS << BeginExpansionDiv;
  ESV.View->print(OS, /*WholeFile=*/false, /*ShowSourceName=*/false,
                  ViewDepth + 1);
  OS << EndExpansionDiv;
}

void SourceCoverageViewHTML::renderInstantiationView(raw_ostream &OS,
                                                     InstantiationView &ISV,
                                                     unsigned ViewDepth) {
  OS << BeginExpansionDiv;
  ISV.View->print(OS, /*WholeFile=*/false, /*ShowSourceName=*/true, ViewDepth);
  OS << EndExpansionDiv;
}
<|endoftext|>


<commit_before>// Copyright 2015 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 "sky/shell/gpu/mojo/rasterizer_mojo.h"

#include "base/trace_event/trace_event.h"
#include "mojo/public/c/gpu/MGL/mgl.h"
#include "mojo/public/c/gpu/MGL/mgl_onscreen.h"
#include "mojo/skia/gl_bindings_skia.h"
#include "third_party/skia/include/core/SkCanvas.h"

namespace sky {
namespace shell {
namespace {

void ContextLostThunk(void* closure) {
  static_cast<RasterizerMojo*>(closure)->OnContextLost();
}

}  // namespace

scoped_ptr<Rasterizer> Rasterizer::Create() {
  return make_scoped_ptr(new RasterizerMojo());
}

RasterizerMojo::RasterizerMojo() : binding_(this), weak_factory_(this) {
}

RasterizerMojo::~RasterizerMojo() {
}

base::WeakPtr<RasterizerMojo> RasterizerMojo::GetWeakPtr() {
  return weak_factory_.GetWeakPtr();
}

base::WeakPtr<Rasterizer> RasterizerMojo::GetWeakRasterizerPtr() {
  return GetWeakPtr();
}

void RasterizerMojo::ConnectToRasterizer (
    mojo::InterfaceRequest<rasterizer::Rasterizer> request ) {
  binding_.Bind(request.Pass());
}

void RasterizerMojo::Draw(uint64_t layer_tree_ptr,
                          const DrawCallback& callback) {
  TRACE_EVENT0("flutter", "RasterizerMojo::Draw");

  scoped_ptr<compositor::LayerTree> layer_tree(
      reinterpret_cast<compositor::LayerTree*>(layer_tree_ptr));

  MGLResizeSurface(layer_tree->frame_size().width(),
                   layer_tree->frame_size().height());
  SkCanvas* canvas = ganesh_canvas_.GetCanvas(0, layer_tree->frame_size());
  sky::compositor::PaintContext::ScopedFrame frame =
      paint_context_.AcquireFrame(*canvas);
  canvas->clear(SK_ColorBLACK);
  layer_tree->root_layer()->Paint(frame);
  canvas->flush();
  MGLSwapBuffers();
  callback.Run();
}

void RasterizerMojo::OnContextProviderAvailable(
    mojo::InterfacePtrInfo<mojo::ContextProvider> context_provider) {
  context_provider_ = mojo::MakeProxy(context_provider.Pass());
  context_provider_->Create(nullptr,
    base::Bind(&RasterizerMojo::OnContextCreated, base::Unretained(this)));
}

void RasterizerMojo::OnContextCreated(mojo::CommandBufferPtr command_buffer) {
  context_ = MGLCreateContext(
      MGL_API_VERSION_GLES2,
      command_buffer.PassInterface().PassHandle().release().value(),
      MGL_NO_CONTEXT, &ContextLostThunk, this,
      mojo::Environment::GetDefaultAsyncWaiter());
  MGLMakeCurrent(context_);
  if (!gr_gl_interface_)
    gr_gl_interface_ = skia::AdoptRef(skia_bindings::CreateMojoSkiaGLBinding());
  ganesh_canvas_.SetGrGLInterface(gr_gl_interface_.get());
}

void RasterizerMojo::OnContextLost() {
  ganesh_canvas_.SetGrGLInterface(nullptr);
  MGLDestroyContext(context_);
  context_ = nullptr;
  context_provider_->Create(nullptr,
    base::Bind(&RasterizerMojo::OnContextCreated, base::Unretained(this)));
}

}  // namespace shell
}  // namespace sky
<commit_msg>Don't crash when RasterizerMojo::Draw happens without a context<commit_after>// Copyright 2015 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 "sky/shell/gpu/mojo/rasterizer_mojo.h"

#include "base/trace_event/trace_event.h"
#include "mojo/public/c/gpu/MGL/mgl.h"
#include "mojo/public/c/gpu/MGL/mgl_onscreen.h"
#include "mojo/skia/gl_bindings_skia.h"
#include "third_party/skia/include/core/SkCanvas.h"

namespace sky {
namespace shell {
namespace {

void ContextLostThunk(void* closure) {
  static_cast<RasterizerMojo*>(closure)->OnContextLost();
}

}  // namespace

scoped_ptr<Rasterizer> Rasterizer::Create() {
  return make_scoped_ptr(new RasterizerMojo());
}

RasterizerMojo::RasterizerMojo() : binding_(this), weak_factory_(this) {
}

RasterizerMojo::~RasterizerMojo() {
}

base::WeakPtr<RasterizerMojo> RasterizerMojo::GetWeakPtr() {
  return weak_factory_.GetWeakPtr();
}

base::WeakPtr<Rasterizer> RasterizerMojo::GetWeakRasterizerPtr() {
  return GetWeakPtr();
}

void RasterizerMojo::ConnectToRasterizer (
    mojo::InterfaceRequest<rasterizer::Rasterizer> request ) {
  binding_.Bind(request.Pass());
}

void RasterizerMojo::Draw(uint64_t layer_tree_ptr,
                          const DrawCallback& callback) {
  TRACE_EVENT0("flutter", "RasterizerMojo::Draw");
  if (!context_) {
    callback.Run();
    return;
  }

  scoped_ptr<compositor::LayerTree> layer_tree(
      reinterpret_cast<compositor::LayerTree*>(layer_tree_ptr));

  MGLResizeSurface(layer_tree->frame_size().width(),
                   layer_tree->frame_size().height());
  SkCanvas* canvas = ganesh_canvas_.GetCanvas(0, layer_tree->frame_size());
  sky::compositor::PaintContext::ScopedFrame frame =
      paint_context_.AcquireFrame(*canvas);
  canvas->clear(SK_ColorBLACK);
  layer_tree->root_layer()->Paint(frame);
  canvas->flush();
  MGLSwapBuffers();
  callback.Run();
}

void RasterizerMojo::OnContextProviderAvailable(
    mojo::InterfacePtrInfo<mojo::ContextProvider> context_provider) {
  context_provider_ = mojo::MakeProxy(context_provider.Pass());
  context_provider_->Create(nullptr,
    base::Bind(&RasterizerMojo::OnContextCreated, base::Unretained(this)));
}

void RasterizerMojo::OnContextCreated(mojo::CommandBufferPtr command_buffer) {
  context_ = MGLCreateContext(
      MGL_API_VERSION_GLES2,
      command_buffer.PassInterface().PassHandle().release().value(),
      MGL_NO_CONTEXT, &ContextLostThunk, this,
      mojo::Environment::GetDefaultAsyncWaiter());
  MGLMakeCurrent(context_);
  if (!gr_gl_interface_)
    gr_gl_interface_ = skia::AdoptRef(skia_bindings::CreateMojoSkiaGLBinding());
  ganesh_canvas_.SetGrGLInterface(gr_gl_interface_.get());
}

void RasterizerMojo::OnContextLost() {
  ganesh_canvas_.SetGrGLInterface(nullptr);
  MGLDestroyContext(context_);
  context_ = nullptr;
  context_provider_->Create(nullptr,
    base::Bind(&RasterizerMojo::OnContextCreated, base::Unretained(this)));
}

}  // namespace shell
}  // namespace sky
<|endoftext|>


<commit_before>/* -------------------------------------------------------------------------- *
 *                       OpenSim:  testAnalyzeTool.cpp                        *
 * -------------------------------------------------------------------------- *
 * The OpenSim API is a toolkit for musculoskeletal modeling and simulation.  *
 * See http://opensim.stanford.edu and the NOTICE file for more information.  *
 * OpenSim is developed at Stanford University and supported by the US        *
 * National Institutes of Health (U54 GM072970, R24 HD065690) and by DARPA    *
 * through the Warrior Web program.                                           *
 *                                                                            *
 * Copyright (c) 2005-2017 Stanford University and the Authors                *
 * Author(s): Ayman Habib, Ajay Seth                                          *
 *                                                                            *
 * 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
#include <OpenSim/Common/CSVFileAdapter.h>
#include <OpenSim/Simulation/Model/Model.h>
#include <OpenSim/Simulation/StatesTrajectory.h>
#include <OpenSim/Actuators/Millard2012EquilibriumMuscle.h>
#include <OpenSim/Tools/AnalyzeTool.h>
#include <OpenSim/Auxiliary/auxiliaryTestFunctions.h>

using namespace OpenSim;
using namespace std;

void testTutorialOne();

// Test different default activations are respected when activation
// states are not provided.
void testTugOfWar(const string& dataFileName, const double& defaultAct);
void reportTendonAndFiberForcesAcrossFiberLengths(const Model& model,
                                           const SimTK::State& state);

int main()
{
    SimTK::Array_<std::string> failures;

    try { testTutorialOne(); }
    catch (const std::exception& e) {
        cout << e.what() << endl; failures.push_back("testTutorialOne");
    }

    // produce passive force-length curve
    try { testTugOfWar("Tug_of_War_ConstantVelocity.sto", 0.01); }
    catch (const std::exception& e) {
        cout << e.what() << endl;
        failures.push_back("testTugOfWar CoordinatesOnly: default_act = 0.01");
    }

    // produced passive + active muscle force-length
    try { testTugOfWar("Tug_of_War_ConstantVelocity.sto", 1.0); }
    catch (const std::exception& e) {
        cout << e.what() << endl;
        failures.push_back("testTugOfWar CoordinatesOnly: default_act = 1.0");
    }

    // now supply activation states which should be used instead of the default
    try { testTugOfWar("Tug_of_War_ConstantVelocity_RampActivation.sto", 0.0); }
    catch (const std::exception& e) {
        cout << e.what() << endl;
        failures.push_back("testTugOfWar with activation state provided");
    }

    if (!failures.empty()) {
        cout << "Done, with failure(s): " << failures << endl;
        return 1;
    }

    cout << "Done" << endl;
    return 0;
}

void testTutorialOne() {
    AnalyzeTool analyze1("PlotterTool.xml");
    analyze1.getModel().print("testAnalyzeTutorialOne.osim");
    analyze1.run();
    /* Once this runs to completion we'll make the test more meaningful by comparing output
    * to a validated standard. Let's make sure we don't crash during run first! -Ayman 5/29/12 */
    Storage resultFiberLength("testPlotterTool/BothLegs__FiberLength.sto");
    Storage standardFiberLength("std_BothLegs_fiberLength.sto");
    CHECK_STORAGE_AGAINST_STANDARD(resultFiberLength, standardFiberLength,
        std::vector<double>(100, 0.0001), __FILE__, __LINE__,
        "testAnalyzeTutorialOne failed");
    // const Model& mdl = analyze1.getModel();
    //mdl.updMultibodySystem()
    analyze1.setStatesFileName("plotterGeneratedStatesHip45.sto");
    //analyze1.setModel(mdl);
    analyze1.setName("BothLegsHip45");
    analyze1.run();
    Storage resultFiberLengthHip45("testPlotterTool/BothLegsHip45__FiberLength.sto");
    Storage standardFiberLength45("std_BothLegsHip45__FiberLength.sto");
    CHECK_STORAGE_AGAINST_STANDARD(resultFiberLengthHip45,
        standardFiberLength45, std::vector<double>(100, 0.0001),
        __FILE__, __LINE__, "testAnalyzeTutorialOne at Hip45 failed");
    cout << "testAnalyzeTutorialOne passed" << endl;
}

void testTugOfWar(const string& dataFileName, const double& defaultAct) {
    AnalyzeTool analyze("Tug_of_War_Setup_Analyze.xml");
    analyze.setCoordinatesFileName("");
    analyze.setStatesFileName("");

    // Access the model and muscle being analyzed
    Model& model = analyze.getModel();
    Millard2012EquilibriumMuscle& muscle =
        static_cast<Millard2012EquilibriumMuscle&>(model.updMuscles()[0]);
    bool isCoordinatesOnly = true;
    // Load in the States used to recompute the results of the Analysis
    Storage dataStore(dataFileName);
    if (dataStore.getColumnLabels().findIndex(muscle.getName() + ".activation") > 0) {
        isCoordinatesOnly = false;
        analyze.setStatesFileName(dataFileName);
        // ramp input starts at 0.0
        muscle.set_minimum_activation(0.0);
    }
    else {
        analyze.setCoordinatesFileName(dataFileName);
    }

    // Test that the default activation is taken into consideration by
    // the Analysis and reflected in the AnalyzeTool solution
    muscle.set_default_activation(defaultAct);
    analyze.run();

    // Load the AnalyzTool results for the muscle's force through time
    TimeSeriesTable_<double> results =
        STOFileAdapter_<double>::
        read("Analyze_Tug_of_War/Tug_of_War_Millard_Iso_ForceReporter_forces.sto");
    assert(results.getNumColumns() == 1);
    SimTK::Vector forces = results.getDependentColumnAtIndex(0);

    // Load input data as StatesTrajectory used to perform the Analysis
    auto statesTraj = StatesTrajectory::createFromStatesStorage(
        model, dataStore, true, false);
    size_t nstates = statesTraj.getSize();

    // muscle active, passive, total muscle and tendon force quantities
    double af, pf, mf, tf, fl, fv;
    af= pf = mf = tf = fl = fv = SimTK::NaN;

    // Tolerance for muscle equilibrium solution
    const double equilTol = muscle.getMaxIsometricForce()*SimTK::SqrtEps;

    // The maximum acceptable change in force between two contiguous states
    const double maxDelta = muscle.getMaxIsometricForce() / 2;

    SimTK::State s = model.getWorkingState();
    // Independently compute the active fiber force at every state
    for (int i = 0; i < nstates; ++i) {
        s = statesTraj[i];
        // When the muscle states are not supplied in the input dataStore
        // (isCoordinatesOnly == true), then set it to its default value.
        if (isCoordinatesOnly) {
            muscle.setActivation(s, muscle.get_default_activation());
        }
        // technically, fiber lengths could be supplied, but this test case
        // (a typical use case) does not and therefore set to its default.
        muscle.setFiberLength(s, muscle.get_default_fiber_length());
        try {
            muscle.computeEquilibrium(s);
        }
        catch (const std::exception& x) {
            // Write out the muscle equilibrium error as a function of
            // fiber lengths.
            reportTendonAndFiberForcesAcrossFiberLengths(model, s);
            throw x;
        }
        model.realizeDynamics(s);

        // Get the fiber-length
        fl = muscle.getFiberLength(s);

        cout << "t = " << s.getTime() << " | fiber_length = " << fl <<
           " : default_fiber_length = " << muscle.get_default_fiber_length() << endl;

        SimTK_ASSERT_ALWAYS(fl >= muscle.getMinimumFiberLength(),
            "Equilibrium failed to compute valid fiber length.");

        if (isCoordinatesOnly) {
            // check that activation is not reset to zero or other value
            SimTK_ASSERT_ALWAYS(muscle.getActivation(s) == defaultAct,
                "Test failed to correctly use the default activation value.");
        }
        else {
            // check that activation used was that supplied by the dataStore
            SimTK_ASSERT_ALWAYS(muscle.getActivation(s) == s.getTime(),
                "Test failed to correctly use the supplied activation values.");
        }

        // get active and passive forces given the default activation
        af = muscle.getActiveFiberForceAlongTendon(s);
        pf = muscle.getPassiveFiberForceAlongTendon(s);
        fv = muscle.getFiberVelocityAlongTendon(s);

        //assert(abs(fv) < SimTK::SqrtEps);

        // now the total muscle force is the active + passive
        mf = af + pf;
        tf = muscle.getTendonForce(s);

        // equilibrium demands tendon and muscle fiber are equivalent
        ASSERT_EQUAL<double>(tf, mf, equilTol);
        // Verify that the current computed and AnalyzeTool reported force are
        // equivalent for the provided motion file
        cout << s.getTime() << " :: muscle-fiber-force: " << mf <<
            " Analyze reported force: " << forces[i] << endl;
        ASSERT_EQUAL<double>(mf, forces[i], equilTol, __FILE__, __LINE__,
            "Total fiber force failed to match reported muscle force.");

        double delta = (i > 0) ? abs(forces[i]-forces[i-1]) : 0;

        SimTK_ASSERT_ALWAYS(delta < maxDelta,
            "Force trajectory has unexplained discontinuity.");
    }
}

/* For the TugOfWar model with a single muscle at a give state report
   how muscle (fiber) and tendon force varies with fiber-length. Also,
   report the difference, which represents the function that the muscle
   equilibrium solve is trying to find a root (zero) for. The intended
   use is to invoke this method when the muscle fails to compute the
   equilibrium fiber-length to help diagnose the cause of the failure.
   */
void reportTendonAndFiberForcesAcrossFiberLengths(const Model& model,
                                           const SimTK::State& state) {
    Millard2012EquilibriumMuscle& muscle =
        static_cast<Millard2012EquilibriumMuscle&>(model.getMuscles()[0]);

    SimTK::State s = state;

    DataTable_<double, double> forcesVsFiberLengthTable;
    std::vector<string> labels{ "fiber_length", "pathLength",
        "tendon_force", "fiber_force", "activation", "activeFiberForce",
        "passiveFiberForce", "equilibriumError"};
    forcesVsFiberLengthTable.setColumnLabels(labels);

    const int N = 100;
    const double maxFiberLength = 2.0*muscle.getOptimalFiberLength();
    double fiberLength = muscle.getMinimumFiberLength();

    const double dl = (maxFiberLength - fiberLength) / N;

    double tendonForce = SimTK::NaN;
    double activeFiberForce = SimTK::NaN;
    double passiveFiberForce = SimTK::NaN;

    SimTK::RowVector row(labels.size(), SimTK::NaN);
    for (int i = 0; i <= N; ++i) {
        fiberLength += dl;
        s.setTime(fiberLength);
        muscle.setFiberLength(s, fiberLength);
        muscle.setActivation(s, muscle.get_default_activation());

        model.realizeDynamics(s);

        tendonForce = muscle.getTendonForce(s);
        activeFiberForce = muscle.getMaxIsometricForce()*
            muscle.getActiveForceLengthMultiplier(s);
        passiveFiberForce = muscle.getPassiveFiberForceAlongTendon(s);

        row[0] = fiberLength; // muscle.getFiberLength(s);
        row[1] = muscle.getLength(s);
        row[2] = tendonForce;
        row[3] = activeFiberForce + passiveFiberForce;
        row[4] = muscle.getActivation(s);
        row[5] = activeFiberForce;
        row[6] = passiveFiberForce;
        row[7] = row[2] - row[3];

        forcesVsFiberLengthTable.appendRow(s.getTime(), row);
    }

    std::string fileName = "forcesVsFiberLength_"
        + std::to_string(muscle.get_default_activation()) + ".sto";

    STOFileAdapter::write(forcesVsFiberLengthTable, fileName);
}
<commit_msg>Address size_t->int loss warning.<commit_after>/* -------------------------------------------------------------------------- *
 *                       OpenSim:  testAnalyzeTool.cpp                        *
 * -------------------------------------------------------------------------- *
 * The OpenSim API is a toolkit for musculoskeletal modeling and simulation.  *
 * See http://opensim.stanford.edu and the NOTICE file for more information.  *
 * OpenSim is developed at Stanford University and supported by the US        *
 * National Institutes of Health (U54 GM072970, R24 HD065690) and by DARPA    *
 * through the Warrior Web program.                                           *
 *                                                                            *
 * Copyright (c) 2005-2017 Stanford University and the Authors                *
 * Author(s): Ayman Habib, Ajay Seth                                          *
 *                                                                            *
 * 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
#include <OpenSim/Common/CSVFileAdapter.h>
#include <OpenSim/Simulation/Model/Model.h>
#include <OpenSim/Simulation/StatesTrajectory.h>
#include <OpenSim/Actuators/Millard2012EquilibriumMuscle.h>
#include <OpenSim/Tools/AnalyzeTool.h>
#include <OpenSim/Auxiliary/auxiliaryTestFunctions.h>

using namespace OpenSim;
using namespace std;

void testTutorialOne();

// Test different default activations are respected when activation
// states are not provided.
void testTugOfWar(const string& dataFileName, const double& defaultAct);
void reportTendonAndFiberForcesAcrossFiberLengths(const Model& model,
                                           const SimTK::State& state);

int main()
{
    SimTK::Array_<std::string> failures;
/*
    try { testTutorialOne(); }
    catch (const std::exception& e) {
        cout << e.what() << endl; failures.push_back("testTutorialOne");
    }
*/
    // produce passive force-length curve
    try { testTugOfWar("Tug_of_War_ConstantVelocity.sto", 0.01); }
    catch (const std::exception& e) {
        cout << e.what() << endl;
        failures.push_back("testTugOfWar CoordinatesOnly: default_act = 0.01");
    }

    // produced passive + active muscle force-length
    try { testTugOfWar("Tug_of_War_ConstantVelocity.sto", 1.0); }
    catch (const std::exception& e) {
        cout << e.what() << endl;
        failures.push_back("testTugOfWar CoordinatesOnly: default_act = 1.0");
    }

    // now supply activation states which should be used instead of the default
    try { testTugOfWar("Tug_of_War_ConstantVelocity_RampActivation.sto", 0.0); }
    catch (const std::exception& e) {
        cout << e.what() << endl;
        failures.push_back("testTugOfWar with activation state provided");
    }

    if (!failures.empty()) {
        cout << "Done, with failure(s): " << failures << endl;
        return 1;
    }

    cout << "Done" << endl;
    return 0;
}

void testTutorialOne() {
    AnalyzeTool analyze1("PlotterTool.xml");
    analyze1.getModel().print("testAnalyzeTutorialOne.osim");
    analyze1.run();
    /* Once this runs to completion we'll make the test more meaningful by comparing output
    * to a validated standard. Let's make sure we don't crash during run first! -Ayman 5/29/12 */
    Storage resultFiberLength("testPlotterTool/BothLegs__FiberLength.sto");
    Storage standardFiberLength("std_BothLegs_fiberLength.sto");
    CHECK_STORAGE_AGAINST_STANDARD(resultFiberLength, standardFiberLength,
        std::vector<double>(100, 0.0001), __FILE__, __LINE__,
        "testAnalyzeTutorialOne failed");
    // const Model& mdl = analyze1.getModel();
    //mdl.updMultibodySystem()
    analyze1.setStatesFileName("plotterGeneratedStatesHip45.sto");
    //analyze1.setModel(mdl);
    analyze1.setName("BothLegsHip45");
    analyze1.run();
    Storage resultFiberLengthHip45("testPlotterTool/BothLegsHip45__FiberLength.sto");
    Storage standardFiberLength45("std_BothLegsHip45__FiberLength.sto");
    CHECK_STORAGE_AGAINST_STANDARD(resultFiberLengthHip45,
        standardFiberLength45, std::vector<double>(100, 0.0001),
        __FILE__, __LINE__, "testAnalyzeTutorialOne at Hip45 failed");
    cout << "testAnalyzeTutorialOne passed" << endl;
}

void testTugOfWar(const string& dataFileName, const double& defaultAct) {
    AnalyzeTool analyze("Tug_of_War_Setup_Analyze.xml");
    analyze.setCoordinatesFileName("");
    analyze.setStatesFileName("");

    // Access the model and muscle being analyzed
    Model& model = analyze.getModel();
    Millard2012EquilibriumMuscle& muscle =
        static_cast<Millard2012EquilibriumMuscle&>(model.updMuscles()[0]);
    bool isCoordinatesOnly = true;
    // Load in the States used to recompute the results of the Analysis
    Storage dataStore(dataFileName);
    if (dataStore.getColumnLabels().findIndex(muscle.getName() + ".activation") > 0) {
        isCoordinatesOnly = false;
        analyze.setStatesFileName(dataFileName);
        // ramp input starts at 0.0
        muscle.set_minimum_activation(0.0);
    }
    else {
        analyze.setCoordinatesFileName(dataFileName);
    }

    // Test that the default activation is taken into consideration by
    // the Analysis and reflected in the AnalyzeTool solution
    muscle.set_default_activation(defaultAct);
    analyze.run();

    // Load the AnalyzTool results for the muscle's force through time
    TimeSeriesTable_<double> results =
        STOFileAdapter_<double>::
        read("Analyze_Tug_of_War/Tug_of_War_Millard_Iso_ForceReporter_forces.sto");
    assert(results.getNumColumns() == 1);
    SimTK::Vector forces = results.getDependentColumnAtIndex(0);

    // Load input data as StatesTrajectory used to perform the Analysis
    auto statesTraj = StatesTrajectory::createFromStatesStorage(
        model, dataStore, true, false);
    size_t nstates = statesTraj.getSize();

    // muscle active, passive, total muscle and tendon force quantities
    double af, pf, mf, tf, fl, fv;
    af= pf = mf = tf = fl = fv = SimTK::NaN;

    // Tolerance for muscle equilibrium solution
    const double equilTol = muscle.getMaxIsometricForce()*SimTK::SqrtEps;

    // The maximum acceptable change in force between two contiguous states
    const double maxDelta = muscle.getMaxIsometricForce() / 2;

    SimTK::State s = model.getWorkingState();
    // Independently compute the active fiber force at every state
    for (int i = 0; i < nstates; ++i) {
        s = statesTraj[i];
        // When the muscle states are not supplied in the input dataStore
        // (isCoordinatesOnly == true), then set it to its default value.
        if (isCoordinatesOnly) {
            muscle.setActivation(s, muscle.get_default_activation());
        }
        // technically, fiber lengths could be supplied, but this test case
        // (a typical use case) does not and therefore set to its default.
        muscle.setFiberLength(s, muscle.get_default_fiber_length());
        try {
            muscle.computeEquilibrium(s);
        }
        catch (const std::exception& x) {
            // Write out the muscle equilibrium error as a function of
            // fiber lengths.
            reportTendonAndFiberForcesAcrossFiberLengths(model, s);
            throw x;
        }
        model.realizeDynamics(s);

        // Get the fiber-length
        fl = muscle.getFiberLength(s);

        cout << "t = " << s.getTime() << " | fiber_length = " << fl <<
           " : default_fiber_length = " << muscle.get_default_fiber_length() << endl;

        SimTK_ASSERT_ALWAYS(fl >= muscle.getMinimumFiberLength(),
            "Equilibrium failed to compute valid fiber length.");

        if (isCoordinatesOnly) {
            // check that activation is not reset to zero or other value
            SimTK_ASSERT_ALWAYS(muscle.getActivation(s) == defaultAct,
                "Test failed to correctly use the default activation value.");
        }
        else {
            // check that activation used was that supplied by the dataStore
            SimTK_ASSERT_ALWAYS(muscle.getActivation(s) == s.getTime(),
                "Test failed to correctly use the supplied activation values.");
        }

        // get active and passive forces given the default activation
        af = muscle.getActiveFiberForceAlongTendon(s);
        pf = muscle.getPassiveFiberForceAlongTendon(s);
        fv = muscle.getFiberVelocityAlongTendon(s);

        //assert(abs(fv) < SimTK::SqrtEps);

        // now the total muscle force is the active + passive
        mf = af + pf;
        tf = muscle.getTendonForce(s);

        // equilibrium demands tendon and muscle fiber are equivalent
        ASSERT_EQUAL<double>(tf, mf, equilTol);
        // Verify that the current computed and AnalyzeTool reported force are
        // equivalent for the provided motion file
        cout << s.getTime() << " :: muscle-fiber-force: " << mf <<
            " Analyze reported force: " << forces[i] << endl;
        ASSERT_EQUAL<double>(mf, forces[i], equilTol, __FILE__, __LINE__,
            "Total fiber force failed to match reported muscle force.");

        double delta = (i > 0) ? abs(forces[i]-forces[i-1]) : 0;

        SimTK_ASSERT_ALWAYS(delta < maxDelta,
            "Force trajectory has unexplained discontinuity.");
    }
}

/* For the TugOfWar model with a single muscle at a give state report
   how muscle (fiber) and tendon force varies with fiber-length. Also,
   report the difference, which represents the function that the muscle
   equilibrium solve is trying to find a root (zero) for. The intended
   use is to invoke this method when the muscle fails to compute the
   equilibrium fiber-length to help diagnose the cause of the failure.
   */
void reportTendonAndFiberForcesAcrossFiberLengths(const Model& model,
                                           const SimTK::State& state) {
    Millard2012EquilibriumMuscle& muscle =
        static_cast<Millard2012EquilibriumMuscle&>(model.getMuscles()[0]);

    SimTK::State s = state;

    DataTable_<double, double> forcesVsFiberLengthTable;
    std::vector<string> labels{ "fiber_length", "pathLength",
        "tendon_force", "fiber_force", "activation", "activeFiberForce",
        "passiveFiberForce", "equilibriumError"};
    forcesVsFiberLengthTable.setColumnLabels(labels);

    const int N = 100;
    const double maxFiberLength = 2.0*muscle.getOptimalFiberLength();
    double fiberLength = muscle.getMinimumFiberLength();

    const double dl = (maxFiberLength - fiberLength) / N;

    double tendonForce = SimTK::NaN;
    double activeFiberForce = SimTK::NaN;
    double passiveFiberForce = SimTK::NaN;

    SimTK::RowVector row(int(labels.size()), SimTK::NaN);
    for (int i = 0; i <= N; ++i) {
        fiberLength += dl;
        s.setTime(fiberLength);
        muscle.setFiberLength(s, fiberLength);
        muscle.setActivation(s, muscle.get_default_activation());

        model.realizeDynamics(s);

        tendonForce = muscle.getTendonForce(s);
        activeFiberForce = muscle.getMaxIsometricForce()*
            muscle.getActiveForceLengthMultiplier(s);
        passiveFiberForce = muscle.getPassiveFiberForceAlongTendon(s);

        row[0] = fiberLength; // muscle.getFiberLength(s);
        row[1] = muscle.getLength(s);
        row[2] = tendonForce;
        row[3] = activeFiberForce + passiveFiberForce;
        row[4] = muscle.getActivation(s);
        row[5] = activeFiberForce;
        row[6] = passiveFiberForce;
        row[7] = row[2] - row[3];

        forcesVsFiberLengthTable.appendRow(s.getTime(), row);
    }

    std::string fileName = "forcesVsFiberLength_"
        + std::to_string(muscle.get_default_activation()) + ".sto";

    STOFileAdapter::write(forcesVsFiberLengthTable, fileName);
}
<|endoftext|>


<commit_before>/*
            This file is part of:
                NoahFrame
            https://github.com/ketoo/NoahGameFrame

   Copyright 2009 - 2020 NoahFrame(NoahGameFrame)

   File creator: lvsheng.huang

   NoahFrame is open-source software and you can redistribute it and/or modify
   it under the terms of the License; besides, anyone who use this file/software must include this copyright announcement.

   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 "NFScheduleModule.h"

void NFScheduleElement::DoHeartBeatEvent(NFINT64 nowTime)
{
	mnRemainCount--;
	mnTriggerTime = nowTime + (NFINT64)(mfIntervalTime * 1000);

#if NF_PLATFORM != NF_PLATFORM_WIN
	NF_CRASH_TRY
#endif

	OBJECT_SCHEDULE_FUNCTOR_PTR cb;
	bool bRet = this->mxObjectFunctor.First(cb);
	while (bRet)
	{

		cb.get()->operator()(self, mstrScheduleName, mfIntervalTime, mnRemainCount);

		bRet = this->mxObjectFunctor.Next(cb);
	}

#if NF_PLATFORM != NF_PLATFORM_WIN
	NF_CRASH_END_TRY
#endif
}

NFScheduleModule::NFScheduleModule(NFIPluginManager* p)
{
	pPluginManager = p;
    m_bIsExecute = true;
}

NFScheduleModule::~NFScheduleModule()
{
	mObjectScheduleMap.ClearAll();
}

bool NFScheduleModule::Init()
{
	m_pLogModule = pPluginManager->FindModule<NFILogModule>();
	m_pKernelModule = pPluginManager->FindModule<NFIKernelModule>();
	m_pSceneModule = pPluginManager->FindModule<NFISceneModule>();

	m_pKernelModule->RegisterCommonClassEvent(this, &NFScheduleModule::OnClassCommonEvent);
	m_pSceneModule->AddSceneGroupDestroyedCallBack(this, &NFScheduleModule::OnGroupCommonEvent);
/*
	std::set<TickElement> test;
	for (int i = 0 ; i < 100;  ++i)
	{
		TickElement element;
		element.triggerTime = m_pKernelModule->Random(1, 50);
		test.insert(element);
	}

	for (auto it = test.begin(); it != test.end(); ++it)
	{
		std::cout << it->triggerTime << std::endl;
	}
 */
	return true;
}

bool NFScheduleModule::Execute()
{
	NFPerformance performanceObject;
	NFINT64 nowTime = NFGetTimeMS();

	static std::vector<TickElement> elements;
	elements.clear();

	for (auto it = mScheduleMap.begin(); it != mScheduleMap.end();)
	{
		if (nowTime >= it->triggerTime)
		{
			//std::cout << nowTime << it->scheduleName << ">>>>>" << it->triggerTime << std::endl;

			auto objectMap = mObjectScheduleMap.GetElement(it->self);
			if (objectMap)
			{
				auto scheduleElement = objectMap->GetElement(it->scheduleName);
				if (scheduleElement)
				{
					m_pLogModule->LogFatal("");

					scheduleElement->DoHeartBeatEvent(nowTime);

					if (scheduleElement->mnRemainCount != 0)
					{
						TickElement element;
						element.scheduleName = scheduleElement->mstrScheduleName;
						element.triggerTime = scheduleElement->mnTriggerTime;
						element.self = scheduleElement->self;

						elements.push_back(element);
					}
				}
			}

			it = mScheduleMap.erase(it);
		}
		else
		{
			break;
		}
	}

	for (auto& item : elements)
	{
		mScheduleMap.insert(item);
	}

	if (performanceObject.CheckTimePoint(1))
	{
		std::ostringstream os;
		os << "---------------module schedule performance problem ";
		os << performanceObject.TimeScope();
		os << "---------- ";
		m_pLogModule->LogWarning(NFGUID(), os, __FUNCTION__, __LINE__);
	}

	return true;
}

bool NFScheduleModule::AddSchedule(const NFGUID self, const std::string& scheduleName, const OBJECT_SCHEDULE_FUNCTOR_PTR& cb, const float time, const int count)
{
	auto objectMap = mObjectScheduleMap.GetElement(self);
	if (!objectMap)
	{
		objectMap = NF_SHARE_PTR< NFMapEx <std::string, NFScheduleElement >>(NF_NEW NFMapEx <std::string, NFScheduleElement >());
		mObjectScheduleMap.AddElement(self, objectMap);
	}

	auto scheduleObject = objectMap->GetElement(scheduleName);
	if (!scheduleObject)
	{
		scheduleObject = NF_SHARE_PTR<NFScheduleElement>(NF_NEW NFScheduleElement());
		scheduleObject->mstrScheduleName = scheduleName;
		scheduleObject->mfIntervalTime = time;
		scheduleObject->mnTriggerTime = NFGetTimeMS() + (NFINT64)(time * 1000);
		scheduleObject->mnRemainCount = count;
		scheduleObject->self = self;

		scheduleObject->mxObjectFunctor.Add(cb);

		objectMap->AddElement(scheduleName, scheduleObject);

		TickElement tickElement;
		tickElement.scheduleName = scheduleObject->mstrScheduleName;
		tickElement.triggerTime = scheduleObject->mnTriggerTime;
		tickElement.self = scheduleObject->self;

		mScheduleMap.insert(tickElement);
	}

	return true;
}

bool NFScheduleModule::RemoveSchedule(const NFGUID self)
{
	return mObjectScheduleMap.RemoveElement(self);
}

bool NFScheduleModule::RemoveSchedule(const NFGUID self, const std::string& scheduleName)
{
	auto objectMap = mObjectScheduleMap.GetElement(self);
	if (objectMap)
	{
		return objectMap->RemoveElement(scheduleName);
	}

	return false;
}

bool NFScheduleModule::ExistSchedule(const NFGUID self, const std::string& scheduleName)
{
	auto objectScheduleMap = mObjectScheduleMap.GetElement(self);
	if (NULL == objectScheduleMap)
	{
		return false;
	}

	return objectScheduleMap->ExistElement(scheduleName);
}

NF_SHARE_PTR<NFScheduleElement> NFScheduleModule::GetSchedule(const NFGUID self, const std::string & scheduleName)
{
	NF_SHARE_PTR< NFMapEx <std::string, NFScheduleElement >> xObjectScheduleMap = mObjectScheduleMap.GetElement(self);
	if (NULL == xObjectScheduleMap)
	{
		return nullptr;
	}

	return xObjectScheduleMap->GetElement(scheduleName);
}

int NFScheduleModule::OnClassCommonEvent(const NFGUID & self, const std::string & className, const CLASS_OBJECT_EVENT classEvent, const NFDataList & var)
{
	if (CLASS_OBJECT_EVENT::COE_DESTROY == classEvent)
	{
		this->RemoveSchedule(self);
	}

	return 0;
}

int NFScheduleModule::OnGroupCommonEvent(const NFGUID &self, const int scene, const int group, const int type, const NFDataList &arg)
{
	this->RemoveSchedule(NFGUID(scene, group));
	return 0;
}
<commit_msg>test code<commit_after>/*
            This file is part of:
                NoahFrame
            https://github.com/ketoo/NoahGameFrame

   Copyright 2009 - 2020 NoahFrame(NoahGameFrame)

   File creator: lvsheng.huang

   NoahFrame is open-source software and you can redistribute it and/or modify
   it under the terms of the License; besides, anyone who use this file/software must include this copyright announcement.

   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 "NFScheduleModule.h"

void NFScheduleElement::DoHeartBeatEvent(NFINT64 nowTime)
{
	mnRemainCount--;
	mnTriggerTime = nowTime + (NFINT64)(mfIntervalTime * 1000);

#if NF_PLATFORM != NF_PLATFORM_WIN
	NF_CRASH_TRY
#endif

	OBJECT_SCHEDULE_FUNCTOR_PTR cb;
	bool bRet = this->mxObjectFunctor.First(cb);
	while (bRet)
	{

		cb.get()->operator()(self, mstrScheduleName, mfIntervalTime, mnRemainCount);

		bRet = this->mxObjectFunctor.Next(cb);
	}

#if NF_PLATFORM != NF_PLATFORM_WIN
	NF_CRASH_END_TRY
#endif
}

NFScheduleModule::NFScheduleModule(NFIPluginManager* p)
{
	pPluginManager = p;
    m_bIsExecute = true;
}

NFScheduleModule::~NFScheduleModule()
{
	mObjectScheduleMap.ClearAll();
}

bool NFScheduleModule::Init()
{
	m_pLogModule = pPluginManager->FindModule<NFILogModule>();
	m_pKernelModule = pPluginManager->FindModule<NFIKernelModule>();
	m_pSceneModule = pPluginManager->FindModule<NFISceneModule>();

	m_pKernelModule->RegisterCommonClassEvent(this, &NFScheduleModule::OnClassCommonEvent);
	m_pSceneModule->AddSceneGroupDestroyedCallBack(this, &NFScheduleModule::OnGroupCommonEvent);
/*
	std::set<TickElement> test;
	for (int i = 0 ; i < 100;  ++i)
	{
		TickElement element;
		element.triggerTime = m_pKernelModule->Random(1, 50);
		test.insert(element);
	}

	for (auto it = test.begin(); it != test.end(); ++it)
	{
		std::cout << it->triggerTime << std::endl;
	}
 */
	return true;
}

bool NFScheduleModule::Execute()
{
	NFPerformance performanceObject;
	NFINT64 nowTime = NFGetTimeMS();

	static std::vector<TickElement> elements;
	elements.clear();

	for (auto it = mScheduleMap.begin(); it != mScheduleMap.end();)
	{
		if (nowTime >= it->triggerTime)
		{
			//std::cout << nowTime << it->scheduleName << ">>>>>" << it->triggerTime << std::endl;

			auto objectMap = mObjectScheduleMap.GetElement(it->self);
			if (objectMap)
			{
				auto scheduleElement = objectMap->GetElement(it->scheduleName);
				if (scheduleElement)
				{
					m_pLogModule->LogFatal(it->scheduleName);

					scheduleElement->DoHeartBeatEvent(nowTime);

					if (scheduleElement->mnRemainCount != 0)
					{
						TickElement element;
						element.scheduleName = scheduleElement->mstrScheduleName;
						element.triggerTime = scheduleElement->mnTriggerTime;
						element.self = scheduleElement->self;

						elements.push_back(element);
					}
				}
			}

			it = mScheduleMap.erase(it);
		}
		else
		{
			break;
		}
	}

	for (auto& item : elements)
	{
		mScheduleMap.insert(item);
	}

	if (performanceObject.CheckTimePoint(1))
	{
		std::ostringstream os;
		os << "---------------module schedule performance problem ";
		os << performanceObject.TimeScope();
		os << "---------- ";
		m_pLogModule->LogWarning(NFGUID(), os, __FUNCTION__, __LINE__);
	}

	return true;
}

bool NFScheduleModule::AddSchedule(const NFGUID self, const std::string& scheduleName, const OBJECT_SCHEDULE_FUNCTOR_PTR& cb, const float time, const int count)
{
	auto objectMap = mObjectScheduleMap.GetElement(self);
	if (!objectMap)
	{
		objectMap = NF_SHARE_PTR< NFMapEx <std::string, NFScheduleElement >>(NF_NEW NFMapEx <std::string, NFScheduleElement >());
		mObjectScheduleMap.AddElement(self, objectMap);
	}

	auto scheduleObject = objectMap->GetElement(scheduleName);
	if (!scheduleObject)
	{
		scheduleObject = NF_SHARE_PTR<NFScheduleElement>(NF_NEW NFScheduleElement());
		scheduleObject->mstrScheduleName = scheduleName;
		scheduleObject->mfIntervalTime = time;
		scheduleObject->mnTriggerTime = NFGetTimeMS() + (NFINT64)(time * 1000);
		scheduleObject->mnRemainCount = count;
		scheduleObject->self = self;

		scheduleObject->mxObjectFunctor.Add(cb);

		objectMap->AddElement(scheduleName, scheduleObject);

		TickElement tickElement;
		tickElement.scheduleName = scheduleObject->mstrScheduleName;
		tickElement.triggerTime = scheduleObject->mnTriggerTime;
		tickElement.self = scheduleObject->self;

		mScheduleMap.insert(tickElement);
	}

	return true;
}

bool NFScheduleModule::RemoveSchedule(const NFGUID self)
{
	return mObjectScheduleMap.RemoveElement(self);
}

bool NFScheduleModule::RemoveSchedule(const NFGUID self, const std::string& scheduleName)
{
	auto objectMap = mObjectScheduleMap.GetElement(self);
	if (objectMap)
	{
		return objectMap->RemoveElement(scheduleName);
	}

	return false;
}

bool NFScheduleModule::ExistSchedule(const NFGUID self, const std::string& scheduleName)
{
	auto objectScheduleMap = mObjectScheduleMap.GetElement(self);
	if (NULL == objectScheduleMap)
	{
		return false;
	}

	return objectScheduleMap->ExistElement(scheduleName);
}

NF_SHARE_PTR<NFScheduleElement> NFScheduleModule::GetSchedule(const NFGUID self, const std::string & scheduleName)
{
	NF_SHARE_PTR< NFMapEx <std::string, NFScheduleElement >> xObjectScheduleMap = mObjectScheduleMap.GetElement(self);
	if (NULL == xObjectScheduleMap)
	{
		return nullptr;
	}

	return xObjectScheduleMap->GetElement(scheduleName);
}

int NFScheduleModule::OnClassCommonEvent(const NFGUID & self, const std::string & className, const CLASS_OBJECT_EVENT classEvent, const NFDataList & var)
{
	if (CLASS_OBJECT_EVENT::COE_DESTROY == classEvent)
	{
		this->RemoveSchedule(self);
	}

	return 0;
}

int NFScheduleModule::OnGroupCommonEvent(const NFGUID &self, const int scene, const int group, const int type, const NFDataList &arg)
{
	this->RemoveSchedule(NFGUID(scene, group));
	return 0;
}
<|endoftext|>


<commit_before>//=====================================================================//
/*!	@file
	@brief	GUI Widget ターミナル
	@author	平松邦仁 (hira@rvf-rc45.net)
*/
//=====================================================================//
#include "widgets/widget_terminal.hpp"
#include "core/glcore.hpp"
#include "widgets/widget_frame.hpp"
#include <boost/foreach.hpp>

namespace gui {

	//-----------------------------------------------------------------//
	/*!
		@brief	１文字出力
		@param[in]	wch	文字
	*/
	//-----------------------------------------------------------------//
	void widget_terminal::output(uint32_t wch)
	{
		terminal_.output(wch);
	}

	//-----------------------------------------------------------------//
	/*!
		@brief	テキストの出力
		@param[in]	text	テキスト
	*/
	//-----------------------------------------------------------------//
	void widget_terminal::output(const std::string& text)
	{
		if(text.empty()) return;

		utils::lstring ls;
		utils::utf8_to_utf32(text, ls);

		BOOST_FOREACH(uint32_t ch, ls) {
			output(ch);
		}
	}

	//-----------------------------------------------------------------//
	/*!
		@brief	初期化
	*/
	//-----------------------------------------------------------------//
	void widget_terminal::initialize()
	{
		at_param().state_.set(widget::state::POSITION_LOCK);
		at_param().state_.set(widget::state::SIZE_LOCK);
		at_param().state_.set(widget::state::RESIZE_H_ENABLE, false);
		at_param().state_.set(widget::state::RESIZE_V_ENABLE, false);
		at_param().state_.set(widget::state::SERVICE);
		at_param().state_.set(widget::state::MOVE_ROOT, false);
		at_param().state_.set(widget::state::RESIZE_ROOT);
		at_param().state_.set(widget::state::CLIP_PARENTS);
		at_param().state_.set(widget::state::AREA_ROOT);

		using namespace gl;
		core& core = core::get_instance();
		fonts& fonts = core.at_fonts();

		fonts.push_font_face();
		fonts.set_font_type(param_.font_);
		fonts.set_font_size(param_.font_height_);
		fonts.enable_proportional(false);
		fonts.set_spaceing(0);
		param_.font_width_ = fonts.get_width(' ');
		fonts.pop_font_face();
	}

	//-----------------------------------------------------------------//
	/*!
		@brief	アップデート
	*/
	//-----------------------------------------------------------------//
	void widget_terminal::update()
	{
		if(wd_.get_focus_widget() == this || wd_.get_focus_widget() == wd_.root_widget(this)) {
			focus_ = true;
		} else {
			focus_ = false;
		}

		if(get_param().parents_ && get_state(widget::state::AREA_ROOT)) {
			if(get_param().parents_->type() == get_type_id<widget_frame>()) {
				// 親になってるフレームを取得
				widget_frame* w = static_cast<widget_frame*>(at_param().parents_);
				if(w) {
					bool resize = false;
					vtx::irect sr;
					w->get_draw_area(sr);
					if(param_.auto_fit_) {
						vtx::ipos ss(sr.size.x / param_.font_width_,
									 sr.size.y / param_.height_);
						ss.x *= param_.font_width_;
//						if(ss.x < w->get_param().resize_min_.x) {
//							ss.x = w->get_param().resize_min_.x / param_.font_width_;
//							ss.x *= param_.font_width_;
//						}
						ss.y *= param_.height_;
//						if(ss.y < w->get_param().resize_min_.y) {
//							ss.y = w->get_param().resize_min_.y / param_.height_;
//							ss.y *= param_.height_;
//						}
						w->set_draw_area(ss);
						w->get_draw_area(sr);
					}
					if(sr.size != get_rect().size) resize = true;
					at_rect() = sr;
					if(resize) {

					}
				}
			}
		}
	}

	//-----------------------------------------------------------------//
	/*!
		@brief	サービス
	*/
	//-----------------------------------------------------------------//
	void widget_terminal::service()
	{
		if(focus_) {
			if(param_.echo_) {
				terminal_.output(wd_.at_keyboard().input());
			}
		}
	}

	//-----------------------------------------------------------------//
	/*!
		@brief	レンダリング
	*/
	//-----------------------------------------------------------------//
	void widget_terminal::render()
	{
		using namespace gl;
		core& core = core::get_instance();
		const vtx::spos& vsz = core.get_size();
		const vtx::spos& siz = core.get_rect().size;
		gl::fonts& fonts = core.at_fonts();

		const widget::param& wp = get_param();

		if(wp.clip_.size.x > 0 && wp.clip_.size.y > 0) {

			glPushMatrix();

			vtx::irect rect;
			if(wp.state_[widget::state::CLIP_PARENTS]) {
				rect.org  = wp.rpos_;
				rect.size = wp.rect_.size;
			} else {
				rect.org.set(0);
				rect.size = wp.rect_.size;
			}

			fonts.push_font_face();
			fonts.set_font_type(param_.font_);
			fonts.set_font_size(param_.font_height_);

			vtx::irect clip_ = wp.clip_;

			int sx = vsz.x / siz.x;
			int sy = vsz.y / siz.y;
			glViewport(clip_.org.x * sx, vsz.y - clip_.org.y * sy - clip_.size.y * sy,
				clip_.size.x * sx, clip_.size.y * sy);
			fonts.setup_matrix(clip_.size.x, clip_.size.y);

			fonts.enable_center(false);
			fonts.enable_proportional(false);

//			tpr.shadow_color_ *= cf.r;
//			tpr.shadow_color_.alpha_scale(cf.a);
			const img::rgbaf& cf = wd_.get_color();
			vtx::spos limit(clip_.size.x / param_.font_width_, clip_.size.y / param_.height_);
			vtx::spos chs(rect.org);
			const vtx::spos& max = terminal_.get_max();
			vtx::spos ofs(0);
			if((max.y + 1) > limit.y) ofs.y = max.y + 1 - limit.y;
			vtx::spos pos;
			for(pos.y = 0; pos.y < limit.y; ++pos.y) {
				for(pos.x = 0; pos.x < limit.x; ++pos.x) {
					const utils::terminal::cha_t& t = terminal_.get_char(pos + ofs);
					img::rgba8 fc = t.fc_;
					fc *= cf.r;
					fc.alpha_scale(cf.a);
					fonts.set_fore_color(fc);
					img::rgba8 bc = t.bc_;
					bc *= cf.r;
					bc.alpha_scale(cf.a);
					fonts.set_back_color(bc);
					if(focus_ && (pos + ofs) == terminal_.get_cursor()) {
						if((interval_ % 40) < 20) {
							fonts.swap_color();
						}
					}
					if(t.cha_ > 0x7f) {
						fonts.pop_font_face();
					}
					int fw = fonts.get_width(t.cha_);
					vtx::irect br(chs, vtx::ipos(fw, param_.height_));
					fonts.draw_back(br);
					chs.x += fonts.draw(chs, t.cha_);
					fonts.swap_color(false);
					if(t.cha_ > 0x7f) {
						fonts.push_font_face();
						fonts.set_font_type(param_.font_);
					}
				}
				chs.y += param_.height_;
				chs.x = rect.org.x;
			}
			++interval_;

			fonts.restore_matrix();
			fonts.pop_font_face();
			glPopMatrix();
			glViewport(0, 0, vsz.x, vsz.y);
		}
	}

	//-----------------------------------------------------------------//
	/*!
		@brief	状態のセーブ
		@param[in]	pre	プリファレンス参照
		@return エラーが無い場合「true」
	*/
	//-----------------------------------------------------------------//
	bool widget_terminal::save(sys::preference& pre)
	{
		return false;
	}

	//-----------------------------------------------------------------//
	/*!
		@brief	状態のロード
		@param[in]	pre	プリファレンス参照
		@return エラーが無い場合「true」
	*/
	//-----------------------------------------------------------------//
	bool widget_terminal::load(const sys::preference& pre)
	{
		return false;
	}
}
<commit_msg>add bs function<commit_after>//=====================================================================//
/*!	@file
	@brief	GUI Widget ターミナル
	@author	平松邦仁 (hira@rvf-rc45.net)
*/
//=====================================================================//
#include "widgets/widget_terminal.hpp"
#include "core/glcore.hpp"
#include "widgets/widget_frame.hpp"
#include <boost/foreach.hpp>

namespace gui {

	//-----------------------------------------------------------------//
	/*!
		@brief	１文字出力
		@param[in]	wch	文字
	*/
	//-----------------------------------------------------------------//
	void widget_terminal::output(uint32_t wch)
	{
		terminal_.output(wch);
	}

	//-----------------------------------------------------------------//
	/*!
		@brief	テキストの出力
		@param[in]	text	テキスト
	*/
	//-----------------------------------------------------------------//
	void widget_terminal::output(const std::string& text)
	{
		if(text.empty()) return;

		auto ls = utils::utf8_to_utf32(text);

		BOOST_FOREACH(uint32_t ch, ls) {
			output(ch);
		}
	}

	//-----------------------------------------------------------------//
	/*!
		@brief	初期化
	*/
	//-----------------------------------------------------------------//
	void widget_terminal::initialize()
	{
		at_param().state_.set(widget::state::POSITION_LOCK);
		at_param().state_.set(widget::state::SIZE_LOCK);
		at_param().state_.set(widget::state::RESIZE_H_ENABLE, false);
		at_param().state_.set(widget::state::RESIZE_V_ENABLE, false);
		at_param().state_.set(widget::state::SERVICE);
		at_param().state_.set(widget::state::MOVE_ROOT, false);
		at_param().state_.set(widget::state::RESIZE_ROOT);
		at_param().state_.set(widget::state::CLIP_PARENTS);
		at_param().state_.set(widget::state::AREA_ROOT);

		using namespace gl;
		core& core = core::get_instance();
		fonts& fonts = core.at_fonts();

		fonts.push_font_face();
		fonts.set_font_type(param_.font_);
		fonts.set_font_size(param_.font_height_);
		fonts.enable_proportional(false);
		fonts.set_spaceing(0);
		param_.font_width_ = fonts.get_width(' ');
		fonts.pop_font_face();
	}

	//-----------------------------------------------------------------//
	/*!
		@brief	アップデート
	*/
	//-----------------------------------------------------------------//
	void widget_terminal::update()
	{
		if(wd_.get_focus_widget() == this || wd_.get_focus_widget() == wd_.root_widget(this)) {
			focus_ = true;
		} else {
			focus_ = false;
		}

		if(get_param().parents_ && get_state(widget::state::AREA_ROOT)) {
			if(get_param().parents_->type() == get_type_id<widget_frame>()) {
				// 親になってるフレームを取得
				widget_frame* w = static_cast<widget_frame*>(at_param().parents_);
				if(w) {
					bool resize = false;
					vtx::irect sr;
					w->get_draw_area(sr);
					if(param_.auto_fit_) {
						vtx::ipos ss(sr.size.x / param_.font_width_,
									 sr.size.y / param_.height_);
						ss.x *= param_.font_width_;
//						if(ss.x < w->get_param().resize_min_.x) {
//							ss.x = w->get_param().resize_min_.x / param_.font_width_;
//							ss.x *= param_.font_width_;
//						}
						ss.y *= param_.height_;
//						if(ss.y < w->get_param().resize_min_.y) {
//							ss.y = w->get_param().resize_min_.y / param_.height_;
//							ss.y *= param_.height_;
//						}
						w->set_draw_area(ss);
						w->get_draw_area(sr);
					}
					if(sr.size != get_rect().size) resize = true;
					at_rect() = sr;
					if(resize) {

					}
				}
			}
		}
	}

	//-----------------------------------------------------------------//
	/*!
		@brief	サービス
	*/
	//-----------------------------------------------------------------//
	void widget_terminal::service()
	{
		if(focus_) {
			if(param_.echo_) {
				terminal_.output(wd_.at_keyboard().input());
			}
		}
	}

	//-----------------------------------------------------------------//
	/*!
		@brief	レンダリング
	*/
	//-----------------------------------------------------------------//
	void widget_terminal::render()
	{
		using namespace gl;
		core& core = core::get_instance();
		const vtx::spos& vsz = core.get_size();
		const vtx::spos& siz = core.get_rect().size;
		gl::fonts& fonts = core.at_fonts();

		const widget::param& wp = get_param();

		if(wp.clip_.size.x > 0 && wp.clip_.size.y > 0) {

			glPushMatrix();

			vtx::irect rect;
			if(wp.state_[widget::state::CLIP_PARENTS]) {
				rect.org  = wp.rpos_;
				rect.size = wp.rect_.size;
			} else {
				rect.org.set(0);
				rect.size = wp.rect_.size;
			}

			fonts.push_font_face();
			fonts.set_font_type(param_.font_);
			fonts.set_font_size(param_.font_height_);

			vtx::irect clip_ = wp.clip_;

			int sx = vsz.x / siz.x;
			int sy = vsz.y / siz.y;
			glViewport(clip_.org.x * sx, vsz.y - clip_.org.y * sy - clip_.size.y * sy,
				clip_.size.x * sx, clip_.size.y * sy);
			fonts.setup_matrix(clip_.size.x, clip_.size.y);

			fonts.enable_center(false);
			fonts.enable_proportional(false);

//			tpr.shadow_color_ *= cf.r;
//			tpr.shadow_color_.alpha_scale(cf.a);
			const img::rgbaf& cf = wd_.get_color();
			vtx::spos limit(clip_.size.x / param_.font_width_, clip_.size.y / param_.height_);
			vtx::spos chs(rect.org);
			const vtx::spos& max = terminal_.get_max();
			vtx::spos ofs(0);
			if((max.y + 1) > limit.y) ofs.y = max.y + 1 - limit.y;
			vtx::spos pos;
			for(pos.y = 0; pos.y < limit.y; ++pos.y) {
				for(pos.x = 0; pos.x < limit.x; ++pos.x) {
					const utils::terminal::cha_t& t = terminal_.get_char(pos + ofs);
					img::rgba8 fc = t.fc_;
					fc *= cf.r;
					fc.alpha_scale(cf.a);
					fonts.set_fore_color(fc);
					img::rgba8 bc = t.bc_;
					bc *= cf.r;
					bc.alpha_scale(cf.a);
					fonts.set_back_color(bc);
					if(focus_ && (pos + ofs) == terminal_.get_cursor()) {
						if((interval_ % 40) < 20) {
							fonts.swap_color();
						}
					}
					auto cha = t.cha_;
					if(cha < 0x20) cha = 0x20;  // 制御コードは「スペース」として扱う
					if(cha > 0x7f) {
						fonts.pop_font_face();
					}
					int fw = fonts.get_width(cha);
					vtx::irect br(chs, vtx::ipos(fw, param_.height_));
					fonts.draw_back(br);
					chs.x += fonts.draw(chs, cha);
					fonts.swap_color(false);
					if(cha > 0x7f) {
						fonts.push_font_face();
						fonts.set_font_type(param_.font_);
					}
				}
				chs.y += param_.height_;
				chs.x = rect.org.x;
			}
			++interval_;

			fonts.restore_matrix();
			fonts.pop_font_face();
			glPopMatrix();
			glViewport(0, 0, vsz.x, vsz.y);
		}
	}

	//-----------------------------------------------------------------//
	/*!
		@brief	状態のセーブ
		@param[in]	pre	プリファレンス参照
		@return エラーが無い場合「true」
	*/
	//-----------------------------------------------------------------//
	bool widget_terminal::save(sys::preference& pre)
	{
		return false;
	}

	//-----------------------------------------------------------------//
	/*!
		@brief	状態のロード
		@param[in]	pre	プリファレンス参照
		@return エラーが無い場合「true」
	*/
	//-----------------------------------------------------------------//
	bool widget_terminal::load(const sys::preference& pre)
	{
		return false;
	}
}
<|endoftext|>