| /* |
| * Copyright (C) 2022 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| * THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include "PathOperation.h" |
| |
| #include "GeometryUtilities.h" |
| #include "SVGElement.h" |
| |
| namespace WebCore { |
| |
| Ref<ReferencePathOperation> ReferencePathOperation::create(const String& url, const AtomString& fragment, const RefPtr<SVGElement> element) |
| { |
| return adoptRef(*new ReferencePathOperation(url, fragment, element)); |
| } |
| |
| |
| ReferencePathOperation::ReferencePathOperation(const String& url, const AtomString& fragment, const RefPtr<SVGElement> element) |
| : PathOperation(Reference) |
| , m_url(url) |
| , m_fragment(fragment) |
| , m_element(element) |
| { |
| } |
| |
| const SVGElement* ReferencePathOperation::element() const |
| { |
| return m_element.get(); |
| } |
| |
| double RayPathOperation::lengthForPath() const |
| { |
| auto boundingBox = m_containingBlockBoundingRect; |
| auto distances = distanceOfPointToSidesOfRect(boundingBox, m_position); |
| |
| switch (m_size) { |
| case Size::ClosestSide: |
| return std::min( { distances.top(), distances.bottom(), distances.left(), distances.right() } ); |
| case Size::FarthestSide: |
| return std::max( { distances.top(), distances.bottom(), distances.left(), distances.right() } ); |
| case Size::FarthestCorner: |
| return std::sqrt(std::pow(std::max(distances.left(), distances.right()), 2) + std::pow(std::max(distances.top(), distances.bottom()), 2)); |
| case Size::ClosestCorner: |
| return std::sqrt(std::pow(std::min(distances.left(), distances.right()), 2) + std::pow(std::min(distances.top(), distances.bottom()), 2)); |
| case Size::Sides: |
| return lengthOfRayIntersectionWithBoundingBox(boundingBox, std::make_pair(m_position, m_angle)); |
| } |
| RELEASE_ASSERT_NOT_REACHED(); |
| } |
| |
| double RayPathOperation::lengthForContainPath(const FloatRect& elementRect, double computedPathLength, const FloatPoint& anchor, const OffsetRotation rotation) const |
| { |
| // Construct vertices of element for determining if they are within the path length |
| // Anchor point as origin |
| auto vertices = verticesForBox(elementRect, anchor); |
| |
| // Rotate vertices depending on offset rotate or angle |
| if (!rotation.hasAuto()) { |
| auto deg = toRelatedAcuteAngle(toPositiveAngle(m_angle - rotation.angle())); |
| auto angle = deg2rad(deg); |
| std::for_each(vertices.begin(), vertices.end(), [angle] (FloatPoint& p) { |
| p.rotate(angle); |
| }); |
| } |
| |
| Vector<std::pair<double, double>, 4> bounds; |
| |
| for (const auto& p : vertices) { |
| // Use equation for circle (offset distance + x)^2 + y^2 <= r^2 to find offset distance that satisfies equation |
| // If no solution for above equation, must minimally increase it, otherwise clamp such that |
| // every point is within path |
| double discriminant = computedPathLength * computedPathLength - p.y() * p.y(); |
| if (discriminant < 0) { |
| // Need to minimally increase path length |
| break; |
| } |
| bounds.append(std::make_pair(-p.x() - std::sqrt(discriminant), -p.x() + std::sqrt(discriminant))); |
| } |
| |
| if (vertices.size() == bounds.size()) { |
| auto lowerBound = std::max_element(bounds.begin(), bounds.end(), |
| [] (std::pair<double, double> const lhs, std::pair<double, double> const rhs) { |
| return lhs.first < rhs.first; |
| })->first; |
| |
| auto upperBound = std::min_element(bounds.begin(), bounds.end(), |
| [] (std::pair<double, double> const lhs, std::pair<double, double> const rhs) { |
| return lhs.second < rhs.second; |
| })->second; |
| |
| if (lowerBound <= upperBound) |
| return std::max(lowerBound, std::min(upperBound, computedPathLength)); |
| } |
| |
| // TODO: Implement minimally increasing path length to allow all vertices to be within such a path length |
| return computedPathLength; |
| } |
| |
| const Path RayPathOperation::pathForReferenceRect(const FloatRect& elementRect, const FloatPoint& anchor, const OffsetRotation rotation) const |
| { |
| Path path; |
| if (m_containingBlockBoundingRect.isZero()) |
| return path; |
| double length = lengthForPath(); |
| if (m_isContaining) |
| length = lengthForContainPath(elementRect, length, anchor, rotation); |
| auto radians = deg2rad(toPositiveAngle(m_angle) - 90.0); |
| auto point = FloatPoint(std::cos(radians) * length, std::sin(radians) * length); |
| path.addLineTo(point); |
| return path; |
| } |
| |
| } // namespace WebCore |