三维频谱marker

This commit is contained in:
2026-08-22 23:43:44 +08:00
parent 7c3f27474b
commit 9540f257e5
12 changed files with 294 additions and 71 deletions
+78 -14
View File
@@ -189,16 +189,39 @@ std::pair<double, double> generator_range(const Json& input, std::string_view mi
if (minimum >= maximum) throw std::invalid_argument(std::string(maximum_key) + " must be greater than " + std::string(minimum_key));
return {minimum, maximum};
}
void generate_spectral_row(std::vector<Plot_Value>& values, std::size_t row,
std::size_t signal_count, double minimum, double maximum,
double noise_standard_deviation, std::mt19937_64& engine) {
if (noise_standard_deviation < 0.0)
throw std::invalid_argument("noise_stddev must not be negative");
std::normal_distribution<double> noise(0.0, noise_standard_deviation);
const double span = maximum - minimum;
const double denominator = static_cast<double>(std::max<std::size_t>(1, values.size() - 1));
for (std::size_t index = 0; index < values.size(); ++index) {
const double x = static_cast<double>(index) / denominator;
double value = minimum + span * 0.10 + noise(engine);
for (std::size_t signal = 0; signal < signal_count; ++signal) {
const double phase = static_cast<double>(signal + 1) / static_cast<double>(signal_count + 1);
const double center = std::clamp(phase + 0.035 * std::sin(row * 0.09 + signal * 1.73), 0.01, 0.99);
const double width = 0.003 + 0.018 * static_cast<double>((signal % 5) + 1) / 5.0;
const double distance = (x - center) / width;
value += span * (0.45 + 0.45 * std::sin(signal * 2.17 + row * 0.037)) * std::exp(-0.5 * distance * distance);
}
values[index] = std::clamp(value, minimum, maximum);
}
}
template <typename Definition>
Json generator_2d_schema() {
Json fields = Json::array();
std::string label;
std::string description;
if constexpr (std::same_as<Definition, Spectrum>) {
label = "生成频谱采样"; description = "生成一条完整功率频谱,样本沿当前频率范围均匀分布";
label = "生成频谱采样"; description = "生成一条含可控噪声底和多个窄带谱峰的完整功率频谱。";
fields.push_back(generator_integer_field("sample_count", "频谱采样点数", "一次频谱更新包含的功率采样点数。", 4096));
fields.push_back(generator_number_field("power_min", "功率下界", "随机功率下界。", -110.0, -1'000'000.0, 1'000'000.0));
fields.push_back(generator_number_field("power_max", "功率上界", "随机功率上界,必须大于下界。", -20.0, -1'000'000.0, 1'000'000.0));
fields.push_back(generator_number_field("power_min", "功率下界", "噪声底和谱峰最终裁剪的功率下界。", -110.0, -1'000'000.0, 1'000'000.0));
fields.push_back(generator_number_field("power_max", "功率上界", "谱峰最终裁剪的功率上界,必须大于下界。", -20.0, -1'000'000.0, 1'000'000.0));
fields.push_back(generator_integer_field("signal_count", "窄带信号数", "叠加在噪声底上的漂移高斯谱峰数量。", 8, 256));
fields.push_back(generator_number_field("noise_stddev", "噪声标准差", "功率噪声的标准差,单位与功率值一致。", 2.0, 0.0, 1'000'000.0));
} else if constexpr (std::same_as<Definition, Frequency_Trace>) {
label = "生成频率轨迹"; description = "按时间顺序生成一组轨迹采样。";
fields.push_back(generator_integer_field("sample_count", "轨迹采样点数", "时间有序的轨迹点数量。", 4096));
@@ -211,18 +234,24 @@ Json generator_2d_schema() {
fields.push_back(generator_integer_field("bins_per_block", "每块频点数", "每个扫频块保存的连续频点数量。", 8, 65'536));
fields.push_back(generator_number_field("power_min", "功率下界", "随机扫频功率下界。", -110.0, -1'000'000.0, 1'000'000.0));
fields.push_back(generator_number_field("power_max", "功率上界", "随机扫频功率上界,必须大于下界。", -20.0, -1'000'000.0, 1'000'000.0));
fields.push_back(generator_integer_field("signal_count", "窄带信号数", "跨扫频块连续分布的谱峰数量。", 8, 256));
fields.push_back(generator_number_field("noise_stddev", "噪声标准差", "扫频噪声底标准差。", 2.0, 0.0, 1'000'000.0));
} else if constexpr (std::same_as<Definition, Afterglow>) {
label = "生成余辉历史"; description = "生成多帧频谱历史,用于测试余辉累积和衰减。";
fields.push_back(generator_integer_field("history_count", "历史频谱帧数", "余辉中保留的历史频谱数量。", 32, 4096));
fields.push_back(generator_integer_field("samples_per_spectrum", "每帧采样点数", "每条历史频谱包含的功率采样点数。", 512, 65'536));
fields.push_back(generator_number_field("power_min", "功率下界", "随机功率值下界。", -110.0, -1'000'000.0, 1'000'000.0));
fields.push_back(generator_number_field("power_max", "功率上界", "随机功率值上界,必须大于下界。", -20.0, -1'000'000.0, 1'000'000.0));
fields.push_back(generator_integer_field("signal_count", "漂移信号数", "在历史帧之间连续漂移的谱峰数量。", 8, 256));
fields.push_back(generator_number_field("noise_stddev", "噪声标准差", "历史频谱噪声底标准差。", 2.0, 0.0, 1'000'000.0));
} else if constexpr (std::same_as<Definition, Waterfall>) {
label = "生成瀑布图历史"; description = "生成带时间刻度的多行频谱数据。";
fields.push_back(generator_integer_field("row_count", "瀑布行数", "瀑布图中保存的时间行数量。", 256, 4096));
fields.push_back(generator_integer_field("bins_per_row", "每行频点数", "每一时间行包含的频率采样点数。", 512, 65'536));
fields.push_back(generator_number_field("power_min", "功率下界", "随机功率值下界。", -110.0, -1'000'000.0, 1'000'000.0));
fields.push_back(generator_number_field("power_max", "功率上界", "随机功率值上界,必须大于下界。", -20.0, -1'000'000.0, 1'000'000.0));
fields.push_back(generator_integer_field("signal_count", "漂移信号数", "沿时间行移动的窄带谱峰数量。", 8, 256));
fields.push_back(generator_number_field("noise_stddev", "噪声标准差", "瀑布噪声底标准差。", 2.0, 0.0, 1'000'000.0));
} else if constexpr (std::same_as<Definition, Constellation_Diagram>) {
label = "生成星座采样"; description = "分别按 I/Q 坐标范围生成随机星座点。";
fields.push_back(generator_integer_field("point_count", "星座点数", "本次写入的 I/Q 采样数量。", 10'000));
@@ -238,20 +267,21 @@ Json generator_2d_schema() {
fields.push_back(generator_number_field("y_min", "Y 坐标下界", "矩形起点 Y 随机范围下界。", 0.0, -1'000'000.0, 1'000'000.0));
fields.push_back(generator_number_field("y_max", "Y 坐标上界", "矩形终点 Y 随机范围上界。", 100.0, -1'000'000.0, 1'000'000.0));
}
return {{"label", std::move(label)}, {"description", std::move(description)}, {"fields", std::move(fields)}};
if (!fields.empty())
fields.push_back(generator_integer_field("seed", "随机种子", "固定种子可重现同一压力数据集,便于对比不同帧策略和像素传输模式。", 42, 4'294'967'295ULL));
return {{"label", std::move(label)}, {"description", std::move(description) + " 可通过数据规模与坐标/数值范围构造可重复的压力负载。"}, {"fields", std::move(fields)}};
}
template <typename Object>
nlohmann::json generate_2d_data(Object& object, const Json& input) {
using Definition = typename Object::Attached_Object;
try {
std::mt19937_64 engine{std::random_device{}()};
std::mt19937_64 engine{generator_count(input, "seed", 4'294'967'295ULL)};
std::size_t generated_count{};
if constexpr (std::same_as<Definition, Spectrum>) {
const auto count = generator_count(input, "sample_count");
const auto [minimum, maximum] = generator_range(input, "power_min", "power_max");
std::uniform_real_distribution<double> distribution(minimum, maximum);
std::vector<Plot_Value> values(count);
std::ranges::generate(values, [&] { return distribution(engine); });
generate_spectral_row(values, 0, generator_count(input, "signal_count", 256), minimum, maximum, generator_number(input, "noise_stddev"), engine);
object.update_samples(values);
generated_count = count;
} else if constexpr (std::same_as<Definition, Frequency_Trace>) {
@@ -269,9 +299,11 @@ nlohmann::json generate_2d_data(Object& object, const Json& input) {
const auto width = generator_count(input, "bins_per_block", 65'536);
if (block_count > 1'000'000 / width) throw std::invalid_argument("sweep data exceeds 1,000,000 samples");
const auto [minimum, maximum] = generator_range(input, "power_min", "power_max");
std::uniform_real_distribution<double> distribution(minimum, maximum);
std::vector<std::vector<Plot_Value>> blocks(block_count, std::vector<Plot_Value>(width));
for (auto& block : blocks) std::ranges::generate(block, [&] { return distribution(engine); });
std::vector<Plot_Value> complete(block_count * width);
generate_spectral_row(complete, 0, generator_count(input, "signal_count", 256), minimum, maximum, generator_number(input, "noise_stddev"), engine);
for (std::size_t block = 0; block < block_count; ++block)
std::ranges::copy_n(complete.begin() + block * width, width, blocks[block].begin());
object.template set<&Sweep_Spectrum::Prop::bins_per_block>(width);
object.template set<&Sweep_Spectrum::Prop::block_count>(block_count);
object.template set<&Sweep_Spectrum::Prop::blocks>(std::move(blocks));
@@ -281,9 +313,11 @@ nlohmann::json generate_2d_data(Object& object, const Json& input) {
const auto width = generator_count(input, "samples_per_spectrum", 65'536);
if (row_count > 1'000'000 / width) throw std::invalid_argument("afterglow data exceeds 1,000,000 samples");
const auto [minimum, maximum] = generator_range(input, "power_min", "power_max");
std::uniform_real_distribution<double> distribution(minimum, maximum);
std::vector<std::vector<Plot_Value>> spectra(row_count, std::vector<Plot_Value>(width));
for (auto& spectrum : spectra) std::ranges::generate(spectrum, [&] { return distribution(engine); });
const auto signal_count = generator_count(input, "signal_count", 256);
const auto noise_stddev = generator_number(input, "noise_stddev");
for (std::size_t row = 0; row < row_count; ++row)
generate_spectral_row(spectra[row], row, signal_count, minimum, maximum, noise_stddev, engine);
object.template set<&Afterglow::Prop::spectra>(std::move(spectra));
generated_count = row_count * width;
} else if constexpr (std::same_as<Definition, Waterfall>) {
@@ -291,12 +325,13 @@ nlohmann::json generate_2d_data(Object& object, const Json& input) {
const auto width = generator_count(input, "bins_per_row", 65'536);
if (row_count > 1'000'000 / width) throw std::invalid_argument("waterfall data exceeds 1,000,000 samples");
const auto [minimum, maximum] = generator_range(input, "power_min", "power_max");
std::uniform_real_distribution<double> distribution(minimum, maximum);
std::vector<Waterfall_Row> rows;
rows.reserve(row_count);
const auto signal_count = generator_count(input, "signal_count", 256);
const auto noise_stddev = generator_number(input, "noise_stddev");
for (std::size_t row = 0; row < row_count; ++row) {
std::vector<Plot_Value> row_values(width);
std::ranges::generate(row_values, [&] { return distribution(engine); });
generate_spectral_row(row_values, row, signal_count, minimum, maximum, noise_stddev, engine);
rows.push_back({static_cast<Plot_Time_Tick>(row), std::move(row_values)});
}
object.template set<&Waterfall::Prop::frequency_bin_count>(width);
@@ -461,9 +496,38 @@ std::shared_ptr<Plot> make_axes_plot(asio::any_io_executor executor) {
components.push_back(make_axis_component("axis-frequency", "频率轴", *frequency));
components.push_back(make_axis_component("axis-value", "数值轴", *numeric));
components.push_back(make_axis_component("axis-time", "时间轴", *time));
Json generator_fields = Json::array({
generator_integer_field("time_sample_count", "时间样本数", "写入时间轴保留窗口的连续时间样本数量。", 16'384),
generator_integer_field("time_step_ms", "时间步长 (ms)", "相邻时间样本之间的毫秒间隔。", 10),
generator_number_field("frequency_min", "频率下界", "频率轴可见坐标下界。", 0.0, -1'000'000'000.0, 1'000'000'000.0),
generator_number_field("frequency_max", "频率上界", "频率轴可见坐标上界,必须大于下界。", 100.0, -1'000'000'000.0, 1'000'000'000.0),
generator_number_field("value_min", "数值下界", "数值轴可见坐标下界。", -100.0, -1'000'000'000.0, 1'000'000'000.0),
generator_number_field("value_max", "数值上界", "数值轴可见坐标上界,必须大于下界。", 0.0, -1'000'000'000.0, 1'000'000'000.0)
});
auto generate = [frequency = frequency.get(), numeric = numeric.get(), time = time.get()](const Json& input) {
try {
const auto sample_count = generator_count(input, "time_sample_count");
const auto time_step = generator_count(input, "time_step_ms");
const auto [frequency_minimum, frequency_maximum] = generator_range(input, "frequency_min", "frequency_max");
const auto [value_minimum, value_maximum] = generator_range(input, "value_min", "value_max");
frequency->template set<&Numeric_Axis::Prop::coordinate_range>(Axis_Range{frequency_minimum, frequency_maximum});
numeric->template set<&Numeric_Axis::Prop::coordinate_range>(Axis_Range{value_minimum, value_maximum});
time->template set<&Time_Axis::Prop::visible_count>(static_cast<Axis_Visible_Count>(sample_count));
constexpr std::uint64_t day_milliseconds = 86'400'000;
for (std::size_t index = 0; index < sample_count; ++index)
time->append_time(Time_Of_Day{static_cast<std::int64_t>((index * time_step) % day_milliseconds)});
return Json{{"success", true}, {"generated_count", sample_count}};
} catch (const std::exception& error) {
return Json{{"success", false}, {"error", error.what()}};
}
};
auto view = std::make_unique<Scene_View_Model<decltype(frequency), decltype(numeric), decltype(time)>>(
std::move(components), std::move(update),
Scene_View_Model<decltype(frequency), decltype(numeric), decltype(time)>::Data_Generator{},
Scene_View_Model<decltype(frequency), decltype(numeric), decltype(time)>::Data_Generator{
Json{{"label", "生成坐标轴压力数据"},
{"description", "按时间样本规模和三个业务坐标范围生成可重复的坐标轴压力负载。"},
{"fields", std::move(generator_fields)}},
std::move(generate)},
std::move(frequency), std::move(numeric), std::move(time));
return std::make_shared<Plot>(std::move(executor), std::move(scene), std::move(view));
}
+34 -16
View File
@@ -44,9 +44,11 @@ Json generator_schema() {
fields.push_back(integer_field("depth", "体数据深度", "体素网格 Z 方向尺寸;总量为宽×高×深。", 64, 1, 256));
fields.push_back(number_field("value_min", "体素值下界", "每个体素随机标量值的下界。", 0.0, -1'000'000.0, 1'000'000.0, 0.01));
fields.push_back(number_field("value_max", "体素值上界", "每个体素随机标量值的上界,必须大于下界。", 1.0, -1'000'000.0, 1'000'000.0, 0.01));
fields.push_back(integer_field("seed", "随机种子", "固定种子可复现体素压力数据,便于跨策略对比。", 42, 1, 4'294'967'295ULL));
return {{"label", "生成体素标量场"}, {"description", "按三维网格尺寸生成连续体数据,不使用随机位置。"}, {"fields", std::move(fields)}};
}
fields.push_back(integer_field("count", "图元数量", "本次替换到 Visual 的图元数量。", 10'000, 1, 1'000'000));
fields.push_back(integer_field("count", "图元数量", "本次替换到 Visual 的图元数量;用于逐级提升 CPU Prepare、GPU 上传与绘制压力", 10'000, 1, 5'000'000));
fields.push_back(integer_field("seed", "随机种子", "固定种子可复现相同空间分布,确保多图与传输模式的性能结果可比较。", 42, 1, 4'294'967'295ULL));
fields.push_back(number_field("x_min", "Scene X 下界", "随机位置在 Scene X 轴上的下界。", -1.0, -1'000'000.0, 1'000'000.0, 0.01));
fields.push_back(number_field("x_max", "Scene X 上界", "随机位置在 Scene X 轴上的上界,必须大于下界。", 1.0, -1'000'000.0, 1'000'000.0, 0.01));
fields.push_back(number_field("y_min", "Scene Y 下界", "随机位置在 Scene Y 轴上的下界。", -1.0, -1'000'000.0, 1'000'000.0, 0.01));
@@ -303,7 +305,7 @@ public:
using Prop = typename Definition::Prop;
using Items = std::remove_cvref_t<decltype(std::declval<Prop>().items)>;
try {
std::mt19937_64 engine{std::random_device{}()};
std::mt19937_64 engine{input_count(input, "seed", 4'294'967'295ULL)};
Items generated;
std::size_t count{};
if constexpr (std::same_as<Definition, Volume_Visual>) {
@@ -324,7 +326,7 @@ public:
prop.field_depth = static_cast<std::uint32_t>(depth);
});
} else {
count = input_count(input, "count");
count = input_count(input, "count", 5'000'000);
const auto& current = visual_->template read_prop<typename Definition::Base_Tag>().items;
if (current.empty()) throw std::invalid_argument("visual has no item template");
const auto prototype = current.front();
@@ -436,6 +438,9 @@ struct Spectrogram_Parameters {
double maximum_frequency_hz{20'000.0};
double minimum_level_db{18.0};
double maximum_level_db{78.0};
double animation_speed{1.0};
std::size_t update_every_n_frames{1};
bool animation_enabled{true};
};
Color spectrogram_color(float value) {
@@ -568,14 +573,17 @@ struct Spectrogram_Data_Generator {
[[nodiscard]] Json schema() const {
Json fields = Json::array();
fields.push_back(integer_field("time_sample_count", "时间采样数", "时间方向网格采样数量;增大后表面沿时间方向更细密", 80, 16, 256));
fields.push_back(integer_field("frequency_bin_count", "频率分箱数", "对数频率方向的网格分箱数量。", 96, 16, 256));
fields.push_back(integer_field("time_sample_count", "时间采样数", "时间方向网格采样数;GPU 顶点数约为 6×(时间采样数-1)×(频率分箱数-1)", parameters.time_sample_count, 16, 1024));
fields.push_back(integer_field("frequency_bin_count", "频率分箱数", "对数频率方向分箱数;与时间采样数共同决定三角形和每次上传的数据量。", parameters.frequency_bin_count, 16, 1024));
fields.push_back(integer_field("ridge_count", "谱峰轨迹数", "生成随时间漂移的窄带谱峰数量。", 5, 1, 12));
fields.push_back(number_field("time_span_seconds", "时间跨度", "X 轴覆盖的时间长度,单位秒。", 4.0, 0.1, 120.0, 0.1));
fields.push_back(number_field("minimum_frequency_hz", "最低频率", "对数频率轴下界,必须大于零。", 10.0, 1.0, 1.0e9, 1.0));
fields.push_back(number_field("maximum_frequency_hz", "最高频率", "对数频率轴上界,必须大于最低频率。", 20'000.0, 2.0, 1.0e9, 10.0));
fields.push_back(number_field("minimum_level_db", "最低声压级", "Z 轴色阶高度下界,单位 dB。", 18.0, -300.0, 300.0, 1.0));
fields.push_back(number_field("maximum_level_db", "最高声压级", "Z 轴色阶高度上界,必须大于下界。", 78.0, -300.0, 300.0, 1.0));
fields.push_back(number_field("time_span_seconds", "时间跨度", "X 轴时间范围,单位秒。", parameters.time_span_seconds, 0.1, 3600.0, 0.1));
fields.push_back(number_field("minimum_frequency_hz", "最低频率", "对数频率轴下界,必须大于零。", parameters.minimum_frequency_hz, 0.001, 1.0e12, 1.0));
fields.push_back(number_field("maximum_frequency_hz", "最高频率", "对数频率轴上界,必须大于最低频率。", parameters.maximum_frequency_hz, 0.002, 1.0e12, 10.0));
fields.push_back(number_field("minimum_level_db", "最低声压级", "Z 轴色阶高度下界,单位 dB。", parameters.minimum_level_db, -1000.0, 1000.0, 1.0));
fields.push_back(number_field("maximum_level_db", "最高声压级", "Z 轴色阶高度上界,必须大于下界。", parameters.maximum_level_db, -1000.0, 1000.0, 1.0));
fields.push_back(number_field("animation_speed", "动态速度倍率", "谱峰随时间运动的倍率;0 表示保持当前相位。", parameters.animation_speed, 0.0, 100.0, 0.1));
fields.push_back(integer_field("update_every_n_frames", "数据更新帧间隔", "每 N 个渲染请求重建并上传一次 Mesh;可分离固定几何绘制与持续数据上传压力。", parameters.update_every_n_frames, 1, 10'000));
fields.push_back({{"key", "animation_enabled"}, {"label", "持续生成动态数据"}, {"description", "关闭后保留生成的数据集,仅测试固定 Mesh 的重复绘制;开启后按更新间隔持续重建。"}, {"editor", "boolean"}, {"editable", true}, {"value", parameters.animation_enabled}});
return {{"label", "生成三维频谱瀑布"},
{"description", "按时间采样、对数频率分箱和声压级范围生成连续 GPU Mesh 表面。"},
{"fields", std::move(fields)}};
@@ -585,19 +593,27 @@ struct Spectrogram_Data_Generator {
const Json& input) {
try {
Spectrogram_Parameters next;
next.time_sample_count = input_count(input, "time_sample_count", 256);
next.frequency_bin_count = input_count(input, "frequency_bin_count", 256);
next.time_sample_count = input_count(input, "time_sample_count", 1024);
next.frequency_bin_count = input_count(input, "frequency_bin_count", 1024);
next.ridge_count = input_count(input, "ridge_count", 12);
next.time_span_seconds = input_number(input, "time_span_seconds");
next.minimum_frequency_hz = input_number(input, "minimum_frequency_hz");
next.maximum_frequency_hz = input_number(input, "maximum_frequency_hz");
next.minimum_level_db = input_number(input, "minimum_level_db");
next.maximum_level_db = input_number(input, "maximum_level_db");
next.animation_speed = input_number(input, "animation_speed");
next.update_every_n_frames = input_count(input, "update_every_n_frames", 10'000);
const auto animation = input.at("animation_enabled");
if (!animation.is_boolean()) throw std::invalid_argument("animation_enabled must be boolean");
next.animation_enabled = animation.get<bool>();
if (!(next.time_span_seconds > 0.0) ||
!(next.minimum_frequency_hz > 0.0) ||
!(next.maximum_frequency_hz > next.minimum_frequency_hz) ||
!(next.maximum_level_db > next.minimum_level_db))
throw std::invalid_argument("spectrogram ranges are invalid");
const auto cells = (next.time_sample_count - 1) * (next.frequency_bin_count - 1);
if (cells > 1'400'000)
throw std::invalid_argument("spectrogram exceeds the 8,400,000 vertex stress-test limit");
parameters = next;
auto mesh = spectrogram_mesh(parameters);
const auto vertex_count = mesh.size();
@@ -623,8 +639,10 @@ struct Spectrogram_Data_Generator {
void update(Impl<Mesh_Visual>& visual, Impl<Axes_3D>&,
Impl<Marker_Visual>* markers,
const Plot_Frame_Request& request) const {
const double animation_seconds = request.time_milliseconds / 1000.0;
const Plot_Frame_Request& request) {
if (!parameters.animation_enabled ||
request.correlation_id % parameters.update_every_n_frames != 0) return;
const double animation_seconds = request.time_milliseconds / 1000.0 * parameters.animation_speed;
auto mesh = spectrogram_mesh(parameters, animation_seconds);
if (visual.update_items(std::move(mesh)) != Mesh_Visual::Update_Items_Result::updated)
throw std::logic_error("animated spectrogram mesh was rejected");
@@ -695,9 +713,9 @@ std::shared_ptr<Plot> make_datoviz_spectrogram_plot(asio::any_io_executor execut
auto marker_result = Impl<Marker_Visual>::Builder{}
.set(&Marker_Visual::Prop::items, std::vector<Marker>{
{{-0.35F, -0.18F, 0.72F}, color(255, 244, 170), 23.0F, 0.0F,
Marker_Shape::diamond},
Marker_Shape::diamond, true},
{{0.28F, 0.34F, 0.86F}, color(88, 236, 211), 21.0F, 0.0F,
Marker_Shape::cross}})
Marker_Shape::cross, true}})
.set(&Marker_Visual::Prop::depth_test, false)
.build();
if (!marker_result)