feat: Added scatter plot visualizer
This commit is contained in:
parent
f9bb4d828a
commit
111eabb84c
@ -24,15 +24,98 @@ namespace hex::plugin::builtin {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
void drawLinePlotVisualizer(pl::ptrn::Pattern &, pl::ptrn::Iteratable &iteratable, bool, const std::vector<pl::core::Token::Literal> &) {
|
template<typename T>
|
||||||
|
std::vector<T> patternToArray(pl::ptrn::Pattern *pattern){
|
||||||
|
std::vector<T> result;
|
||||||
|
result.resize(pattern->getChildren().size());
|
||||||
|
|
||||||
|
if (dynamic_cast<pl::ptrn::PatternPadding*>(pattern) != nullptr && pattern->getSize() == 0)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
if (auto iteratable = dynamic_cast<pl::ptrn::Iteratable*>(pattern); iteratable != nullptr) {
|
||||||
|
iteratable->forEachEntry(0, iteratable->getEntryCount(), [&](u64, pl::ptrn::Pattern *entry) {
|
||||||
|
const auto children = entry->getChildren();
|
||||||
|
for (const auto &[offset, child] : children) {
|
||||||
|
auto startOffset = child->getOffset();
|
||||||
|
|
||||||
|
child->setOffset(offset);
|
||||||
|
ON_SCOPE_EXIT { child->setOffset(startOffset); };
|
||||||
|
|
||||||
|
T value;
|
||||||
|
if constexpr (std::floating_point<T>)
|
||||||
|
value = child->getValue().toFloatingPoint();
|
||||||
|
else if constexpr (std::signed_integral<T>)
|
||||||
|
value = child->getValue().toSigned();
|
||||||
|
else if constexpr (std::unsigned_integral<T>)
|
||||||
|
value = child->getValue().toUnsigned();
|
||||||
|
else
|
||||||
|
static_assert(hex::always_false<T>::value, "Invalid type");
|
||||||
|
|
||||||
|
result.push_back(value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
result.resize(pattern->getSize() / sizeof(float));
|
||||||
|
pattern->getEvaluator()->readData(pattern->getOffset(), result.data(), result.size() * sizeof(float), pattern->getSection());
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
std::vector<T> sampleData(const std::vector<T> &data, size_t count) {
|
||||||
|
size_t stride = std::max(1.0, double(data.size()) / count);
|
||||||
|
|
||||||
|
std::vector<T> result;
|
||||||
|
result.reserve(count);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < data.size(); i += stride) {
|
||||||
|
result.push_back(data[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
void drawLinePlotVisualizer(pl::ptrn::Pattern &, pl::ptrn::Iteratable &, bool shouldReset, const std::vector<pl::core::Token::Literal> &arguments) {
|
||||||
|
static std::vector<float> values;
|
||||||
|
auto dataPattern = arguments[0].toPattern();
|
||||||
|
|
||||||
if (ImPlot::BeginPlot("##plot", ImVec2(400, 250), ImPlotFlags_NoChild | ImPlotFlags_CanvasOnly)) {
|
if (ImPlot::BeginPlot("##plot", ImVec2(400, 250), ImPlotFlags_NoChild | ImPlotFlags_CanvasOnly)) {
|
||||||
|
|
||||||
|
if (shouldReset) {
|
||||||
|
values.clear();
|
||||||
|
values = sampleData(patternToArray<float>(dataPattern), ImPlot::GetPlotSize().x * 4);
|
||||||
|
}
|
||||||
|
|
||||||
ImPlot::SetupAxes("X", "Y", ImPlotAxisFlags_AutoFit, ImPlotAxisFlags_AutoFit);
|
ImPlot::SetupAxes("X", "Y", ImPlotAxisFlags_AutoFit, ImPlotAxisFlags_AutoFit);
|
||||||
|
|
||||||
ImPlot::PlotLineG("##line", [](void *data, int idx) -> ImPlotPoint {
|
ImPlot::PlotLine("##line", values.data(), values.size());
|
||||||
auto &iteratable = *static_cast<pl::ptrn::Iteratable *>(data);
|
|
||||||
return { static_cast<double>(idx), iteratable.getEntry(idx)->getValue().toFloatingPoint() };
|
ImPlot::EndPlot();
|
||||||
}, &iteratable, iteratable.getEntryCount());
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void drawScatterPlotVisualizer(pl::ptrn::Pattern &, pl::ptrn::Iteratable &, bool shouldReset, const std::vector<pl::core::Token::Literal> &arguments) {
|
||||||
|
static std::vector<float> xValues, yValues;
|
||||||
|
|
||||||
|
auto xPattern = arguments[0].toPattern();
|
||||||
|
auto yPattern = arguments[1].toPattern();
|
||||||
|
|
||||||
|
if (ImPlot::BeginPlot("##plot", ImVec2(400, 250), ImPlotFlags_NoChild | ImPlotFlags_CanvasOnly)) {
|
||||||
|
|
||||||
|
if (shouldReset) {
|
||||||
|
xValues.clear(); yValues.clear();
|
||||||
|
xValues = sampleData(patternToArray<float>(xPattern), ImPlot::GetPlotSize().x * 4);
|
||||||
|
yValues = sampleData(patternToArray<float>(yPattern), ImPlot::GetPlotSize().x * 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImPlot::SetupAxes("X", "Y", ImPlotAxisFlags_AutoFit, ImPlotAxisFlags_AutoFit);
|
||||||
|
|
||||||
|
ImPlot::PlotScatter("##scatter", xValues.data(), yValues.data(), xValues.size());
|
||||||
|
|
||||||
ImPlot::EndPlot();
|
ImPlot::EndPlot();
|
||||||
}
|
}
|
||||||
@ -57,10 +140,7 @@ namespace hex::plugin::builtin {
|
|||||||
auto width = arguments[1].toUnsigned();
|
auto width = arguments[1].toUnsigned();
|
||||||
auto height = arguments[2].toUnsigned();
|
auto height = arguments[2].toUnsigned();
|
||||||
|
|
||||||
std::vector<u8> data;
|
auto data = patternToArray<u8>(&pattern);
|
||||||
data.resize(width * height * 4);
|
|
||||||
|
|
||||||
pattern.getEvaluator()->readData(pattern.getOffset(), data.data(), data.size(), pattern.getSection());
|
|
||||||
texture = ImGui::Texture(data.data(), data.size(), width, height);
|
texture = ImGui::Texture(data.data(), data.size(), width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,9 +167,7 @@ namespace hex::plugin::builtin {
|
|||||||
if (cs_open(static_cast<cs_arch>(architecture), static_cast<cs_mode>(mode), &capstone) == CS_ERR_OK) {
|
if (cs_open(static_cast<cs_arch>(architecture), static_cast<cs_mode>(mode), &capstone) == CS_ERR_OK) {
|
||||||
cs_option(capstone, CS_OPT_SKIPDATA, CS_OPT_ON);
|
cs_option(capstone, CS_OPT_SKIPDATA, CS_OPT_ON);
|
||||||
|
|
||||||
std::vector<u8> data;
|
auto data = patternToArray<u8>(&pattern);
|
||||||
data.resize(pattern.getSize());
|
|
||||||
pattern.getEvaluator()->readData(pattern.getOffset(), data.data(), data.size(), pattern.getSection());
|
|
||||||
cs_insn *instructions = nullptr;
|
cs_insn *instructions = nullptr;
|
||||||
|
|
||||||
size_t instructionCount = cs_disasm(capstone, data.data(), data.size(), baseAddress, 0, &instructions);
|
size_t instructionCount = cs_disasm(capstone, data.data(), data.size(), baseAddress, 0, &instructions);
|
||||||
@ -125,44 +203,6 @@ namespace hex::plugin::builtin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
static std::vector<T> patternToArray(pl::ptrn::Pattern *pattern){
|
|
||||||
std::vector<T> result;
|
|
||||||
result.resize(pattern->getChildren().size());
|
|
||||||
|
|
||||||
if (dynamic_cast<pl::ptrn::PatternPadding*>(pattern) && pattern->getSize() == 0)
|
|
||||||
return result;
|
|
||||||
|
|
||||||
if (auto iteratable = dynamic_cast<pl::ptrn::Iteratable*>(pattern); iteratable != nullptr) {
|
|
||||||
iteratable->forEachEntry(0, iteratable->getEntryCount(), [&](u64, pl::ptrn::Pattern *entry) {
|
|
||||||
const auto children = entry->getChildren();
|
|
||||||
for (const auto &[offset, child] : children) {
|
|
||||||
auto startOffset = child->getOffset();
|
|
||||||
|
|
||||||
child->setOffset(offset);
|
|
||||||
ON_SCOPE_EXIT { child->setOffset(startOffset); };
|
|
||||||
|
|
||||||
T value;
|
|
||||||
if constexpr (std::floating_point<T>)
|
|
||||||
value = child->getValue().toFloatingPoint();
|
|
||||||
else if constexpr (std::signed_integral<T>)
|
|
||||||
value = child->getValue().toSigned();
|
|
||||||
else if constexpr (std::unsigned_integral<T>)
|
|
||||||
value = child->getValue().toUnsigned();
|
|
||||||
else
|
|
||||||
static_assert(hex::always_false<T>::value, "Invalid type");
|
|
||||||
|
|
||||||
result.push_back(value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
result.resize(pattern->getSize() / sizeof(float));
|
|
||||||
pattern->getEvaluator()->readData(pattern->getOffset(), result.data(), result.size() * sizeof(float), pattern->getSection());
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
void draw3DVisualizer(pl::ptrn::Pattern &, pl::ptrn::Iteratable &, bool shouldReset, const std::vector<pl::core::Token::Literal> &arguments) {
|
void draw3DVisualizer(pl::ptrn::Pattern &, pl::ptrn::Iteratable &, bool shouldReset, const std::vector<pl::core::Token::Literal> &arguments) {
|
||||||
auto verticesPattern = arguments[1].toPattern();
|
auto verticesPattern = arguments[1].toPattern();
|
||||||
auto indicesPattern = arguments[2].toPattern();
|
auto indicesPattern = arguments[2].toPattern();
|
||||||
@ -290,7 +330,7 @@ namespace hex::plugin::builtin {
|
|||||||
auto channels = arguments[2].toUnsigned();
|
auto channels = arguments[2].toUnsigned();
|
||||||
auto sampleRate = arguments[3].toUnsigned();
|
auto sampleRate = arguments[3].toUnsigned();
|
||||||
|
|
||||||
static std::vector<i16> waveData;
|
static std::vector<i16> waveData, sampledData;
|
||||||
static ma_device audioDevice;
|
static ma_device audioDevice;
|
||||||
static ma_device_config deviceConfig;
|
static ma_device_config deviceConfig;
|
||||||
static bool shouldStop = false;
|
static bool shouldStop = false;
|
||||||
@ -303,6 +343,7 @@ namespace hex::plugin::builtin {
|
|||||||
resetTask = TaskManager::createTask("Visualizing...", TaskManager::NoProgress, [=](Task &) {
|
resetTask = TaskManager::createTask("Visualizing...", TaskManager::NoProgress, [=](Task &) {
|
||||||
ma_device_stop(&audioDevice);
|
ma_device_stop(&audioDevice);
|
||||||
waveData = patternToArray<i16>(wavePattern);
|
waveData = patternToArray<i16>(wavePattern);
|
||||||
|
sampledData = sampleData(waveData, 300_scaled * 4);
|
||||||
index = 0;
|
index = 0;
|
||||||
|
|
||||||
deviceConfig = ma_device_config_init(ma_device_type_playback);
|
deviceConfig = ma_device_config_init(ma_device_type_playback);
|
||||||
@ -340,12 +381,7 @@ namespace hex::plugin::builtin {
|
|||||||
index = dragPos;
|
index = dragPos;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImPlot::PlotLineG("##audio", [](void *data, int idx) -> ImPlotPoint {
|
ImPlot::PlotLine("##audio", sampledData.data(), sampledData.size());
|
||||||
const auto &waveData = *static_cast<std::vector<i16> *>(data);
|
|
||||||
|
|
||||||
auto stride = std::max(1.0, (double(waveData.size()) / ImPlot::GetPlotSize().x)) / 2;
|
|
||||||
return { double(idx * stride), double(waveData[u64(idx * stride)]) };
|
|
||||||
}, &waveData, waveData.size() / (std::max(1.0, (double(waveData.size()) / ImPlot::GetPlotSize().x)) / 2));
|
|
||||||
|
|
||||||
ImPlot::EndPlot();
|
ImPlot::EndPlot();
|
||||||
}
|
}
|
||||||
@ -386,7 +422,7 @@ namespace hex::plugin::builtin {
|
|||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
if (resetTask.isRunning())
|
if (resetTask.isRunning())
|
||||||
ImGui::TextSpinner("Loading...");
|
ImGui::TextSpinner("");
|
||||||
else
|
else
|
||||||
ImGui::TextFormatted("{:02d}:{:02d} / {:02d}:{:02d}",
|
ImGui::TextFormatted("{:02d}:{:02d} / {:02d}:{:02d}",
|
||||||
(index / sampleRate) / 60, (index / sampleRate) % 60,
|
(index / sampleRate) / 60, (index / sampleRate) % 60,
|
||||||
@ -396,7 +432,8 @@ namespace hex::plugin::builtin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void registerPatternLanguageVisualizers() {
|
void registerPatternLanguageVisualizers() {
|
||||||
ContentRegistry::PatternLanguage::addVisualizer("line_plot", drawLinePlotVisualizer, 0);
|
ContentRegistry::PatternLanguage::addVisualizer("line_plot", drawLinePlotVisualizer, 1);
|
||||||
|
ContentRegistry::PatternLanguage::addVisualizer("scatter_plot", drawScatterPlotVisualizer, 2);
|
||||||
ContentRegistry::PatternLanguage::addVisualizer("image", drawImageVisualizer, 0);
|
ContentRegistry::PatternLanguage::addVisualizer("image", drawImageVisualizer, 0);
|
||||||
ContentRegistry::PatternLanguage::addVisualizer("bitmap", drawBitmapVisualizer, 3);
|
ContentRegistry::PatternLanguage::addVisualizer("bitmap", drawBitmapVisualizer, 3);
|
||||||
ContentRegistry::PatternLanguage::addVisualizer("disassembler", drawDisassemblyVisualizer, 4);
|
ContentRegistry::PatternLanguage::addVisualizer("disassembler", drawDisassemblyVisualizer, 4);
|
||||||
|
Loading…
Reference in New Issue
Block a user