blob: 92863aee43a4d6aaf750ab14da4e4ceb141e69f3 [file] [log] [blame]
/*
* 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