This commit is contained in:
Ilya Shakhat 2025-03-16 15:51:13 +00:00 committed by GitHub
commit f493f9e18e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 174 additions and 33 deletions

View file

@ -479,8 +479,10 @@ void RouteRenderer::RenderSubroute(ref_ptr<dp::GraphicsContext> context, ref_ptr
params.m_fakeColor = glsl::ToVec4(df::GetColorConstant(kRouteFakeColor));
params.m_fakeOutlineColor = glsl::ToVec4(df::GetColorConstant(kRouteFakeOutlineColor));
ref_ptr<dp::GpuProgram> prg = mng->GetProgram(style.m_pattern.m_isDashed ?
gpu::Program::RouteDash : gpu::Program::Route);
auto routeType = subrouteData->m_subroute->m_routeType;
ref_ptr<dp::GpuProgram> prg = mng->GetProgram(
(routeType == df::RouteType::Pedestrian || routeType == df::RouteType::Bicycle)? gpu::Program::RouteDifficulty:
style.m_pattern.m_isDashed ? gpu::Program::RouteDash : gpu::Program::Route);
prg->Bind();
dp::ApplyState(context, prg, state);
mng->GetParamsSetter()->Apply(context, prg, params);

View file

@ -578,6 +578,24 @@ drape_ptr<df::SubrouteData> RouteShape::CacheRoute(ref_ptr<dp::GraphicsContext>
segmentsColors.emplace_back(color.GetRedF(), color.GetGreenF(), color.GetBlueF(), alpha);
}
}
else if (!subroute->m_slopes.empty())
{
segmentsColors.reserve(endIndex - startIndex);
for (size_t i = startIndex; i < endIndex; ++i)
{
auto slope = abs(subroute->m_slopes[i]);
dp::Color color;
if (slope < 0.1)
color = dp::Color::Transparent();
else if (slope < 0.2)
color = dp::Color(0xDF, 0xE2, 0x00, 0xFF);
else if (slope < 0.3)
color = dp::Color(0xD5, 0x5F, 0x00, 0xFF);
else
color = dp::Color(0xD5, 0x1F, 0x00, 0xFF);
segmentsColors.emplace_back(color.GetRedF(), color.GetGreenF(), color.GetBlueF(), color.GetAlphaF());
}
}
auto subrouteData = make_unique_dp<df::SubrouteData>();
subrouteData->m_subrouteId = subrouteId;
@ -594,7 +612,8 @@ drape_ptr<df::SubrouteData> RouteShape::CacheRoute(ref_ptr<dp::GraphicsContext>
static_cast<float>(subroute->m_baseDepthIndex * rs::kDepthPerSubroute),
geometryBufferData);
auto state = CreateRenderState(subroute->m_style[styleIndex].m_pattern.m_isDashed ?
auto state = CreateRenderState(!subroute->m_slopes.empty()? gpu::Program::RouteDifficulty:
subroute->m_style[styleIndex].m_pattern.m_isDashed ?
gpu::Program::RouteDash : gpu::Program::Route, DepthLayer::GeometryLayer);
state.SetColorTexture(textures->GetSymbolsTexture());

View file

@ -142,6 +142,7 @@ struct Subroute
m2::PolylineD m_polyline;
std::vector<double> m_turns;
std::vector<traffic::SpeedGroup> m_traffic;
std::vector<float> m_slopes;
double m_baseDistance = 0.0;
double m_baseDepthIndex = 0.0;
float m_maxPixelWidth = -1.0f;

View file

@ -112,7 +112,8 @@ void ReflectChartData(vector<double> & chartData)
bool NormalizeChartData(vector<double> const & distanceDataM,
geometry::Altitudes const & altitudeDataM, size_t resultPointCount,
vector<double> & uniformAltitudeDataM)
vector<double> & uniformAltitudeDataM,
vector<double> & uniformSlopeDataM)
{
double constexpr kEpsilon = 1e-6;
@ -134,11 +135,13 @@ bool NormalizeChartData(vector<double> const & distanceDataM,
return true;
}
auto const calculateAltitude = [&](double distFormStartM) {
using TAltitudeSlope = pair<double, double>;
auto const calculateAltitudeSlope = [&](double distFormStartM) {
if (distFormStartM <= distanceDataM.front())
return static_cast<double>(altitudeDataM.front());
return TAltitudeSlope(static_cast<double>(altitudeDataM.front()), 0);
if (distFormStartM >= distanceDataM.back())
return static_cast<double>(altitudeDataM.back());
return TAltitudeSlope(static_cast<double>(altitudeDataM.back()), 0);
auto const lowerIt = lower_bound(distanceDataM.cbegin(), distanceDataM.cend(), distFormStartM);
size_t const nextPointIdx = distance(distanceDataM.cbegin(), lowerIt);
@ -146,20 +149,24 @@ bool NormalizeChartData(vector<double> const & distanceDataM,
size_t const prevPointIdx = nextPointIdx - 1;
if (base::AlmostEqualAbs(distanceDataM[prevPointIdx], distanceDataM[nextPointIdx], kEpsilon))
return static_cast<double>(altitudeDataM[prevPointIdx]);
return TAltitudeSlope(static_cast<double>(altitudeDataM[prevPointIdx]), 0);
double const k = (altitudeDataM[nextPointIdx] - altitudeDataM[prevPointIdx]) /
(distanceDataM[nextPointIdx] - distanceDataM[prevPointIdx]);
return static_cast<double>(altitudeDataM[prevPointIdx]) +
k * (distFormStartM - distanceDataM[prevPointIdx]);
return TAltitudeSlope(static_cast<double>(altitudeDataM[prevPointIdx]) +
k * (distFormStartM - distanceDataM[prevPointIdx]), k);
};
double const routeLenM = distanceDataM.back();
uniformAltitudeDataM.resize(resultPointCount);
uniformSlopeDataM.resize(resultPointCount);
double const stepLenM = resultPointCount <= 1 ? 0.0 : routeLenM / (resultPointCount - 1);
for (size_t i = 0; i < resultPointCount; ++i)
uniformAltitudeDataM[i] = calculateAltitude(static_cast<double>(i) * stepLenM);
for (size_t i = 0; i < resultPointCount; ++i) {
auto altitudeSlope = calculateAltitudeSlope(static_cast<double>(i) * stepLenM);
uniformAltitudeDataM[i] = altitudeSlope.first;
uniformSlopeDataM[i] = altitudeSlope.second;
}
return true;
}
@ -212,6 +219,7 @@ bool GenerateYAxisChartData(uint32_t height, double minMetersPerPxl,
}
bool GenerateChartByPoints(uint32_t width, uint32_t height, vector<m2::PointD> const & geometry,
vector<double> const & slopes,
MapStyle mapStyle, vector<uint8_t> & frameBuffer)
{
frameBuffer.clear();
@ -221,7 +229,7 @@ bool GenerateChartByPoints(uint32_t width, uint32_t height, vector<m2::PointD> c
agg::rgba8 const kBackgroundColor = agg::rgba8(255, 255, 255, 0);
agg::rgba8 const kLineColor = GetLineColor(mapStyle);
agg::rgba8 const kCurveColor = GetCurveColor(mapStyle);
double constexpr kLineWidthPxl = 2.0;
double constexpr kLineWidthPxl = 3.0;
using TBlender = BlendAdaptor<agg::rgba8, agg::order_rgba>;
using TPixelFormat = agg::pixfmt_custom_blend_rgba<TBlender, agg::rendering_buffer>;
@ -264,14 +272,30 @@ bool GenerateChartByPoints(uint32_t width, uint32_t height, vector<m2::PointD> c
agg::render_scanlines_aa_solid(rasterizer, scanline, baseRenderer, kCurveColor);
// Chart line.
TPath path_adaptor(geometry, false);
TStroke stroke(path_adaptor);
stroke.width(kLineWidthPxl);
stroke.line_cap(agg::round_cap);
stroke.line_join(agg::round_join);
for (size_t i = 1; i < geometry.size(); ++i) {
auto x = geometry[i].x;
auto y = geometry[i].y;
auto slope = abs(slopes[i]);
agg::path_storage segment;
segment.move_to(x, y);
segment.line_to(geometry[i-1].x, geometry[i-1].y);
agg::conv_stroke<agg::path_storage> strokeSegment(segment);
strokeSegment.width(kLineWidthPxl);
rasterizer.add_path(strokeSegment);
agg::rgba8 color;
if (slope < 0.1)
color = kLineColor;
else if (slope < 0.2)
color = agg::rgba8(0xDF, 0xE2, 0x00, 0xFF);
else if (slope < 0.3)
color = agg::rgba8(0xD5, 0x5F, 0x00, 0xFF);
else
color = agg::rgba8(0xD5, 0x1F, 0x00, 0xFF);
agg::render_scanlines_aa_solid(rasterizer, scanline, baseRenderer, color);
}
rasterizer.add_path(stroke);
agg::render_scanlines_aa_solid(rasterizer, scanline, baseRenderer, kLineColor);
return true;
}
@ -287,7 +311,8 @@ bool GenerateChart(uint32_t width, uint32_t height, vector<double> const & dista
}
vector<double> uniformAltitudeDataM;
if (!NormalizeChartData(distanceDataM, altitudeDataM, width, uniformAltitudeDataM))
vector<double> uniformSlopeDataM;
if (!NormalizeChartData(distanceDataM, altitudeDataM, width, uniformAltitudeDataM, uniformSlopeDataM))
return false;
vector<double> yAxisDataPxl;
@ -305,6 +330,6 @@ bool GenerateChart(uint32_t width, uint32_t height, vector<double> const & dista
geometry[i] = m2::PointD(i * oneSegLenPix, yAxisDataPxl[i]);
}
return GenerateChartByPoints(width, height, geometry, mapStyle, frameBuffer);
return GenerateChartByPoints(width, height, geometry, uniformSlopeDataM, mapStyle, frameBuffer);
}
} // namespace maps

View file

@ -21,7 +21,8 @@ void ReflectChartData(std::vector<double> & chartData);
/// This method is used to generalize and evenly distribute points of the chart.
bool NormalizeChartData(std::vector<double> const & distanceDataM,
geometry::Altitudes const & altitudeDataM, size_t resultPointCount,
std::vector<double> & uniformAltitudeDataM);
std::vector<double> & uniformAltitudeDataM,
std::vector<double> & uniformSlopeDataM);
/// \brief fills |yAxisDataPxl|. |yAxisDataPxl| is formed to pevent displaying
/// big waves on the chart in case of small deviation in absolute values in |yAxisData|.
@ -42,7 +43,9 @@ bool GenerateYAxisChartData(uint32_t height, double minMetersPerPxl,
/// \param frameBuffer is a vector for a result image. It's resized in this method.
/// It's filled with RGBA(8888) image date.
bool GenerateChartByPoints(uint32_t width, uint32_t height,
std::vector<m2::PointD> const & geometry, MapStyle mapStyle,
std::vector<m2::PointD> const & geometry,
std::vector<double> const & slopes,
MapStyle mapStyle,
std::vector<uint8_t> & frameBuffer);
bool GenerateChart(uint32_t width, uint32_t height, std::vector<double> const & distanceDataM,

View file

@ -81,7 +81,8 @@ UNIT_TEST(NormalizeChartData_SmokeTest)
geometry::Altitudes const altitudeDataM = {0, 0, 0};
vector<double> uniformAltitudeDataM;
TEST(maps::NormalizeChartData(distanceDataM, altitudeDataM, 2 /* resultPointCount */, uniformAltitudeDataM),
vector<double> uniformSlopeDataM;
TEST(maps::NormalizeChartData(distanceDataM, altitudeDataM, 2 /* resultPointCount */, uniformAltitudeDataM, uniformSlopeDataM),
());
vector<double> const expectedUniformAltitudeDataM = {0.0, 0.0};
@ -94,7 +95,8 @@ UNIT_TEST(NormalizeChartData_NoResultPointTest)
geometry::Altitudes const altitudeDataM = {0, 0, 0};
vector<double> uniformAltitudeDataM;
TEST(maps::NormalizeChartData(distanceDataM, altitudeDataM, 0 /* resultPointCount */, uniformAltitudeDataM),
vector<double> uniformSlopeDataM;
TEST(maps::NormalizeChartData(distanceDataM, altitudeDataM, 0 /* resultPointCount */, uniformAltitudeDataM, uniformSlopeDataM),
());
TEST(uniformAltitudeDataM.empty(), ());
@ -106,7 +108,8 @@ UNIT_TEST(NormalizeChartData_NoPointTest)
geometry::Altitudes const altitudeDataM = {};
vector<double> uniformAltitudeDataM;
TEST(maps::NormalizeChartData(distanceDataM, altitudeDataM, 2 /* resultPointCount */, uniformAltitudeDataM),
vector<double> uniformSlopeDataM;
TEST(maps::NormalizeChartData(distanceDataM, altitudeDataM, 2 /* resultPointCount */, uniformAltitudeDataM, uniformSlopeDataM),
());
TEST(uniformAltitudeDataM.empty(), ());
@ -118,7 +121,8 @@ UNIT_TEST(NormalizeChartData_Test)
geometry::Altitudes const altitudeDataM = {-9, 0, 9, 18};
vector<double> uniformAltitudeDataM;
TEST(maps::NormalizeChartData(distanceDataM, altitudeDataM, 10 /* resultPointCount */, uniformAltitudeDataM),
vector<double> uniformSlopeDataM;
TEST(maps::NormalizeChartData(distanceDataM, altitudeDataM, 10 /* resultPointCount */, uniformAltitudeDataM, uniformSlopeDataM),
());
vector<double> const expectedUniformAltitudeDataM = {-9.0, -6.0, -3.0, 0.0, 3.0,
@ -158,11 +162,12 @@ UNIT_TEST(GenerateYAxisChartData_Test)
UNIT_TEST(GenerateChartByPoints_NoGeometryTest)
{
vector<m2::PointD> const geometry = {};
vector<double> const slopes = {};
size_t constexpr width = 100;
size_t constexpr height = 40;
vector<uint8_t> frameBuffer;
TEST(maps::GenerateChartByPoints(width, height, geometry, MapStyleDefaultLight /* mapStyle */, frameBuffer), ());
TEST(maps::GenerateChartByPoints(width, height, geometry, slopes, MapStyleDefaultLight /* mapStyle */, frameBuffer), ());
TestAngleColors(width, height, frameBuffer, 255 /* expectedR */, 255 /* expectedG */,
255 /* expectedB */, 0 /* expectedA */);
}
@ -170,11 +175,12 @@ UNIT_TEST(GenerateChartByPoints_NoGeometryTest)
UNIT_TEST(GenerateChartByPoints_OnePointTest)
{
vector<m2::PointD> const geometry = {{20.0, 20.0}};
vector<double> const slopes = {0.0};
size_t constexpr width = 40;
size_t constexpr height = 40;
vector<uint8_t> frameBuffer;
TEST(maps::GenerateChartByPoints(width, height, geometry, MapStyleDefaultLight /* mapStyle */, frameBuffer), ());
TEST(maps::GenerateChartByPoints(width, height, geometry, slopes, MapStyleDefaultLight /* mapStyle */, frameBuffer), ());
TestAngleColors(width, height, frameBuffer, 255 /* expectedR */, 255 /* expectedG */,
255 /* expectedB */, 0 /* expectedA */);
}
@ -182,12 +188,13 @@ UNIT_TEST(GenerateChartByPoints_OnePointTest)
UNIT_TEST(GenerateChartByPoints_Test)
{
vector<m2::PointD> const geometry = {{0.0, 0.0}, {10.0, 10.0}};
vector<double> const slopes = {1.0, 0.0};
size_t constexpr width = 40;
size_t constexpr height = 40;
vector<uint8_t> frameBuffer;
TEST(maps::GenerateChartByPoints(width, height, geometry, MapStyleDefaultLight /* mapStyle */, frameBuffer), ());
TEST(maps::GenerateChartByPoints(width, height, geometry, slopes, MapStyleDefaultLight /* mapStyle */, frameBuffer), ());
TEST_EQUAL(frameBuffer.size(), width * height * kAltitudeChartBPP, ());
TEST(IsColor(frameBuffer, 0 /* startColorIdx */, 30 /* expectedR */, 150 /* expectedG */,

View file

@ -81,6 +81,29 @@ void FillTrafficForRendering(vector<RouteSegment> const & segments,
traffic.push_back(s.GetTraffic());
}
void FillSlopesForRendering(Route::SubrouteAttrs const & subrouteAttrs,
vector<RouteSegment> const & segments,
vector<float> & slopes)
{
slopes.clear();
slopes.reserve(segments.size());
auto prevAltitude = subrouteAttrs.GetStart().GetAltitude();
auto prevPoint = subrouteAttrs.GetStart().GetPoint();
for (auto const & s: segments)
{
auto altitude = s.GetJunction().GetAltitude();
auto point = s.GetJunction().GetPoint();
auto height = altitude - prevAltitude;
auto length = mercator::DistanceOnEarth(prevPoint, point);
float slope = (length <= 1 || abs(height) <= 1)? 0.0f: height / length;
slopes.push_back(slope);
prevAltitude = altitude;
prevPoint = point;
}
}
RouteMarkData GetLastPassedPoint(BookmarkManager * bmManager, vector<RouteMarkData> const & points)
{
ASSERT_GREATER_OR_EQUAL(points.size(), 2, ());
@ -710,13 +733,15 @@ bool RoutingManager::InsertRoute(Route const & route)
case RouterType::Pedestrian:
{
subroute->m_routeType = df::RouteType::Pedestrian;
subroute->AddStyle(df::SubrouteStyle(df::kRoutePedestrian, df::RoutePattern(4.0, 2.0)));
subroute->AddStyle(df::SubrouteStyle(df::kRoutePedestrian));
FillSlopesForRendering(route.GetSubrouteAttrs(subrouteIndex), segments, subroute->m_slopes);
break;
}
case RouterType::Bicycle:
{
subroute->m_routeType = df::RouteType::Bicycle;
subroute->AddStyle(df::SubrouteStyle(df::kRouteBicycle, df::RoutePattern(8.0, 2.0)));
subroute->AddStyle(df::SubrouteStyle(df::kRouteBicycle));
FillSlopesForRendering(route.GetSubrouteAttrs(subrouteIndex), segments, subroute->m_slopes);
FillTurnsDistancesForRendering(segments, subroute->m_baseDistance, subroute->m_turns);
break;
}

View file

@ -39,6 +39,7 @@ set(shader_files
GL/route_arrow.fsh.glsl
GL/route_arrow.vsh.glsl
GL/route_dash.fsh.glsl
GL/route_difficulty.fsh.glsl
GL/route_marker.fsh.glsl
GL/route_marker.vsh.glsl
GL/ruler.vsh.glsl

View file

@ -0,0 +1,34 @@
// Warning! Beware to use this shader. "discard" command may significally reduce performance.
// Unfortunately some CG algorithms cannot be implemented on OpenGL ES 2.0 without discarding
// fragments from depth buffer.
varying vec3 v_length;
varying vec4 v_color;
#ifdef SAMSUNG_GOOGLE_NEXUS
uniform sampler2D u_colorTex;
#endif
uniform vec4 u_color;
uniform vec2 u_pattern;
uniform vec4 u_maskColor;
uniform vec2 u_fakeBorders;
uniform vec4 u_fakeColor;
const float kAntialiasingThreshold = 0.92;
void main()
{
if (v_length.x < v_length.z)
discard;
vec2 coefs = step(v_length.xx, u_fakeBorders);
coefs.y = 1.0 - coefs.y;
vec4 mainColor = mix(u_color, u_fakeColor, coefs.x);
mainColor = mix(mainColor, u_fakeColor, coefs.y);
float a = (1.0 - smoothstep(kAntialiasingThreshold, 1.0, abs(v_length.y))) * 0.8;
vec4 color = vec4(mix(mainColor.rgb, v_color.rgb, v_color.a), a);
gl_FragColor = samsungGoogleNexusWorkaround(color);
}

View file

@ -27,6 +27,7 @@ Transit transit.vsh.glsl transit.fsh.glsl
TransitMarker transit_marker.vsh.glsl transit_marker.fsh.glsl
Route route.vsh.glsl route.fsh.glsl
RouteDash route.vsh.glsl route_dash.fsh.glsl
RouteDifficulty route.vsh.glsl route_difficulty.fsh.glsl
RouteArrow route_arrow.vsh.glsl route_arrow.fsh.glsl
RouteMarker route_marker.vsh.glsl route_marker.fsh.glsl
CirclePoint circle_point.vsh.glsl circle_point.fsh.glsl

View file

@ -118,6 +118,25 @@ fragment float4 fsRouteDash(const RouteFragment_T in [[stage_in]],
return color;
}
fragment float4 fsRouteDifficulty(const RouteFragment_T in [[stage_in]],
constant Uniforms_T & uniforms [[buffer(1)]])
{
if (in.lengthParams.x < in.lengthParams.z)
discard_fragment();
constexpr float kAntialiasingThreshold = 0.92;
float2 fb = uniforms.u_fakeBorders;
float2 coefs = step(in.lengthParams.xx, fb);
coefs.y = 1.0 - coefs.y;
float4 mainColor = mix(uniforms.u_color, uniforms.u_fakeColor, coefs.x);
mainColor = mix(mainColor, uniforms.u_fakeColor, coefs.y);
float a = (1.0 - smoothstep(kAntialiasingThreshold, 1.0, abs(v_length.y))) * 0.8;
float4 color = float4(mix(mainColor.rgb, in.color.rgb, in.color.a), a);
return color;
}
// RouteArrow
typedef struct

View file

@ -76,6 +76,7 @@ std::array<ProgramInfo, static_cast<size_t>(Program::ProgramsCount)> const kMeta
ProgramInfo("vsTransitMarker", "fsTransitMarker", {{0, 2}}), // TransitMarker
ProgramInfo("vsRoute", "fsRoute", {{0, 3}}), // Route
ProgramInfo("vsRoute", "fsRouteDash", {{0, 3}}), // RouteDash
ProgramInfo("vsRoute", "fsRouteDifficulty", {{0, 3}}), // RouteDifficulty
ProgramInfo("vsRouteArrow", "fsRouteArrow", {{0, 2}}), // RouteArrow
ProgramInfo("vsRouteMarker", "fsRouteMarker", {{0, 2}}), // RouteMarker
ProgramInfo("vsCirclePoint", "fsCirclePoint", {{0, 0}, {1, 2}}), // CirclePoint

View file

@ -111,6 +111,7 @@ struct ALIGNMENT RouteProgramParams
BIND_PROGRAMS(RouteProgramParams,
Program::Route,
Program::RouteDash,
Program::RouteDifficulty,
Program::RouteArrow,
Program::RouteMarker)
};

View file

@ -39,6 +39,7 @@ enum class Program
TransitMarker,
Route,
RouteDash,
RouteDifficulty,
RouteArrow,
RouteMarker,
CirclePoint,
@ -103,6 +104,7 @@ inline std::string DebugPrint(Program p)
case Program::TransitMarker: return "TransitMarker";
case Program::Route: return "Route";
case Program::RouteDash: return "RouteDash";
case Program::RouteDifficulty: return "RouteDifficulty";
case Program::RouteArrow: return "RouteArrow";
case Program::RouteMarker: return "RouteMarker";
case Program::CirclePoint: return "CirclePoint";