#include #include #include #include #include #include #include #include #include #include #include #include #include namespace mbgl { using namespace style; RenderLineLayer::RenderLineLayer(Immutable _impl) : RenderLayer(style::LayerType::Line, _impl), unevaluated(impl().paint.untransitioned()) { } const style::LineLayer::Impl& RenderLineLayer::impl() const { return static_cast(*baseImpl); } std::unique_ptr RenderLineLayer::createBucket(const BucketParameters& parameters, const std::vector& layers) const { return std::make_unique(parameters, layers, impl().layout); } void RenderLineLayer::transition(const TransitionParameters& parameters) { unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated)); } void RenderLineLayer::evaluate(const PropertyEvaluationParameters& parameters) { style::Properties::Unevaluated extra(unevaluated.get()); auto dashArrayParams = parameters; dashArrayParams.useIntegerZoom = true; evaluated = RenderLinePaintProperties::PossiblyEvaluated( unevaluated.evaluate(parameters).concat(extra.evaluate(dashArrayParams))); passes = (evaluated.get().constantOr(1.0) > 0 && evaluated.get().constantOr(Color::black()).a > 0 && evaluated.get().constantOr(1.0) > 0) ? RenderPass::Translucent : RenderPass::None; } bool RenderLineLayer::hasTransition() const { return unevaluated.hasTransition(); } void RenderLineLayer::render(PaintParameters& parameters, RenderSource*) { if (parameters.pass == RenderPass::Opaque) { return; } for (const RenderTile& tile : renderTiles) { assert(dynamic_cast(tile.tile.getBucket(*baseImpl))); LineBucket& bucket = *reinterpret_cast(tile.tile.getBucket(*baseImpl)); auto draw = [&] (auto& program, auto&& uniformValues) { auto& programInstance = program.get(evaluated); const auto& paintPropertyBinders = bucket.paintPropertyBinders.at(getID()); const auto allUniformValues = programInstance.computeAllUniformValues( std::move(uniformValues), paintPropertyBinders, evaluated, parameters.state.getZoom() ); const auto allAttributeBindings = programInstance.computeAllAttributeBindings( *bucket.vertexBuffer, paintPropertyBinders, evaluated ); checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings)); programInstance.draw( parameters.context, gl::Triangles(), parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), parameters.stencilModeForClipping(tile.clip), parameters.colorModeForRenderPass(), *bucket.indexBuffer, bucket.segments, allUniformValues, allAttributeBindings, getID() ); }; if (!evaluated.get().from.empty()) { const LinePatternCap cap = bucket.layout.get() == LineCapType::Round ? LinePatternCap::Round : LinePatternCap::Square; LinePatternPos posA = parameters.lineAtlas.getDashPosition(evaluated.get().from, cap); LinePatternPos posB = parameters.lineAtlas.getDashPosition(evaluated.get().to, cap); parameters.lineAtlas.bind(parameters.context, 0); draw(parameters.programs.lineSDF, LineSDFProgram::uniformValues( evaluated, parameters.pixelRatio, tile, parameters.state, parameters.pixelsToGLUnits, posA, posB, parameters.lineAtlas.getSize().width)); } else if (!evaluated.get().from.empty()) { optional posA = parameters.imageManager.getPattern(evaluated.get().from); optional posB = parameters.imageManager.getPattern(evaluated.get().to); if (!posA || !posB) return; parameters.imageManager.bind(parameters.context, 0); draw(parameters.programs.linePattern, LinePatternProgram::uniformValues( evaluated, tile, parameters.state, parameters.pixelsToGLUnits, parameters.imageManager.getPixelSize(), *posA, *posB)); } else { draw(parameters.programs.line, LineProgram::uniformValues( evaluated, tile, parameters.state, parameters.pixelsToGLUnits)); } } } optional offsetLine(const GeometryCollection& rings, const double offset) { if (offset == 0) return {}; GeometryCollection newRings; Point zero(0, 0); for (const auto& ring : rings) { newRings.emplace_back(); auto& newRing = newRings.back(); for (auto i = ring.begin(); i != ring.end(); i++) { auto& p = *i; Point aToB = i == ring.begin() ? zero : util::perp(util::unit(convertPoint(p - *(i - 1)))); Point bToC = i + 1 == ring.end() ? zero : util::perp(util::unit(convertPoint(*(i + 1) - p))); Point extrude = util::unit(aToB + bToC); const double cosHalfAngle = extrude.x * bToC.x + extrude.y * bToC.y; extrude *= (1.0 / cosHalfAngle); newRing.push_back(convertPoint(extrude * offset) + p); } } return newRings; } bool RenderLineLayer::queryIntersectsFeature( const GeometryCoordinates& queryGeometry, const GeometryTileFeature& feature, const float zoom, const TransformState& transformState, const float pixelsToTileUnits, const mat4&) const { // Translate query geometry auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry( queryGeometry, evaluated.get(), evaluated.get(), transformState.getAngle(), pixelsToTileUnits); // Evaluate function auto offset = evaluated.get() .evaluate(feature, zoom, style::LineOffset::defaultValue()) * pixelsToTileUnits; // Apply offset to geometry auto offsetGeometry = offsetLine(feature.getGeometries(), offset); // Test intersection const float halfWidth = getLineWidth(feature, zoom) / 2.0 * pixelsToTileUnits; return util::polygonIntersectsBufferedMultiLine( translatedQueryGeometry.value_or(queryGeometry), offsetGeometry.value_or(feature.getGeometries()), halfWidth); } float RenderLineLayer::getLineWidth(const GeometryTileFeature& feature, const float zoom) const { float lineWidth = evaluated.get() .evaluate(feature, zoom, style::LineWidth::defaultValue()); float gapWidth = evaluated.get() .evaluate(feature, zoom, style::LineGapWidth::defaultValue()); if (gapWidth) { return gapWidth + 2 * lineWidth; } else { return lineWidth; } } } // namespace mbgl