commit
97b76ca0d0
@ -0,0 +1,428 @@
|
||||
use euclid::Angle;
|
||||
use lyon_geom::{
|
||||
Arc, ArcFlags, CubicBezierSegment, Line, LineSegment, Point, Scalar, SvgArc, Transform, Vector,
|
||||
};
|
||||
|
||||
pub enum ArcOrLineSegment<S> {
|
||||
Arc(SvgArc<S>),
|
||||
Line(LineSegment<S>),
|
||||
}
|
||||
|
||||
fn arc_from_endpoints_and_tangents<S: Scalar>(
|
||||
from: Point<S>,
|
||||
from_tangent: Vector<S>,
|
||||
to: Point<S>,
|
||||
to_tangent: Vector<S>,
|
||||
) -> Option<SvgArc<S>> {
|
||||
let from_to = (from - to).length();
|
||||
let incenter = {
|
||||
let from_tangent = Line {
|
||||
point: from,
|
||||
vector: from_tangent,
|
||||
};
|
||||
let to_tangent = Line {
|
||||
point: to,
|
||||
vector: to_tangent,
|
||||
};
|
||||
|
||||
let intersection = from_tangent.intersection(&to_tangent)?;
|
||||
let from_intersection = (from - intersection).length();
|
||||
let to_intersection = (to - intersection).length();
|
||||
|
||||
(((from * to_intersection).to_vector()
|
||||
+ (to * from_intersection).to_vector()
|
||||
+ (intersection * from_to).to_vector())
|
||||
/ (from_intersection + to_intersection + from_to))
|
||||
.to_point()
|
||||
};
|
||||
|
||||
let get_perpendicular_bisector = |a, b| {
|
||||
let vector: Vector<S> = a - b;
|
||||
let perpendicular_vector = Vector::from([-vector.y, vector.x]).normalize();
|
||||
Line {
|
||||
point: LineSegment { from: a, to: b }.sample(S::HALF),
|
||||
vector: perpendicular_vector,
|
||||
}
|
||||
};
|
||||
|
||||
let from_incenter_bisector = get_perpendicular_bisector(from, incenter);
|
||||
let to_incenter_bisector = get_perpendicular_bisector(to, incenter);
|
||||
let center = from_incenter_bisector.intersection(&to_incenter_bisector)?;
|
||||
|
||||
let radius = (from - center).length();
|
||||
|
||||
// Use the 2D determinant + dot product to identify winding direction
|
||||
// See https://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands for
|
||||
// a nice visual explanation of large arc and sweep
|
||||
let flags = {
|
||||
let from_center = (from - center).normalize();
|
||||
let to_center = (to - center).normalize();
|
||||
|
||||
let det = from_center.x * to_center.y - from_center.y * to_center.x;
|
||||
let dot = from_center.dot(to_center);
|
||||
let atan2 = det.atan2(dot);
|
||||
ArcFlags {
|
||||
large_arc: atan2.abs() >= S::PI(),
|
||||
sweep: atan2.is_sign_positive(),
|
||||
}
|
||||
};
|
||||
|
||||
Some(SvgArc {
|
||||
from,
|
||||
to,
|
||||
radii: Vector::splat(radius),
|
||||
// This is a circular arc
|
||||
x_rotation: Angle::zero(),
|
||||
flags,
|
||||
})
|
||||
}
|
||||
|
||||
pub trait FlattenWithArcs<S> {
|
||||
fn flattened(&self, tolerance: S) -> Vec<ArcOrLineSegment<S>>;
|
||||
}
|
||||
|
||||
impl<S> FlattenWithArcs<S> for CubicBezierSegment<S>
|
||||
where
|
||||
S: Scalar + Copy,
|
||||
{
|
||||
/// Implementation of [Modeling of Bézier Curves Using a Combination of Linear and Circular Arc Approximations](https://sci-hub.st/https://doi.org/10.1109/CGIV.2012.20)
|
||||
///
|
||||
/// There are some slight deviations like using monotonic ranges instead of bounding by inflection points.
|
||||
///
|
||||
/// Kaewsaiha, P., & Dejdumrong, N. (2012). Modeling of Bézier Curves Using a Combination of Linear and Circular Arc Approximations. 2012 Ninth International Conference on Computer Graphics, Imaging and Visualization. doi:10.1109/cgiv.2012.20
|
||||
///
|
||||
fn flattened(&self, tolerance: S) -> Vec<ArcOrLineSegment<S>> {
|
||||
if (self.to - self.from).square_length() < S::EPSILON {
|
||||
return vec![];
|
||||
} else if self.is_linear(tolerance) {
|
||||
return vec![ArcOrLineSegment::Line(self.baseline())];
|
||||
}
|
||||
let mut acc = vec![];
|
||||
|
||||
self.for_each_monotonic_range(|range| {
|
||||
let inner_bezier = self.split_range(range);
|
||||
|
||||
if (inner_bezier.to - inner_bezier.from).square_length() < S::EPSILON {
|
||||
return;
|
||||
} else if inner_bezier.is_linear(tolerance) {
|
||||
acc.push(ArcOrLineSegment::Line(inner_bezier.baseline()));
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(svg_arc) = arc_from_endpoints_and_tangents(
|
||||
inner_bezier.from,
|
||||
inner_bezier.derivative(S::ZERO),
|
||||
inner_bezier.to,
|
||||
inner_bezier.derivative(S::ONE),
|
||||
)
|
||||
.filter(|svg_arc| {
|
||||
let arc = svg_arc.to_arc();
|
||||
let mut max_deviation = S::ZERO;
|
||||
// TODO: find a better way to check tolerance
|
||||
// Ideally: derivative of |f(x) - g(x)| and look at 0 crossings
|
||||
for i in 1..20 {
|
||||
let t = S::from(i).unwrap() / S::from(20).unwrap();
|
||||
max_deviation =
|
||||
max_deviation.max((arc.sample(t) - inner_bezier.sample(t)).length());
|
||||
}
|
||||
max_deviation < tolerance
|
||||
}) {
|
||||
acc.push(ArcOrLineSegment::Arc(svg_arc));
|
||||
} else {
|
||||
let (left, right) = inner_bezier.split(S::HALF);
|
||||
acc.append(&mut FlattenWithArcs::flattened(&left, tolerance));
|
||||
acc.append(&mut FlattenWithArcs::flattened(&right, tolerance));
|
||||
}
|
||||
});
|
||||
acc
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> FlattenWithArcs<S> for SvgArc<S>
|
||||
where
|
||||
S: Scalar,
|
||||
{
|
||||
fn flattened(&self, tolerance: S) -> Vec<ArcOrLineSegment<S>> {
|
||||
if (self.to - self.from).square_length() < S::EPSILON {
|
||||
return vec![];
|
||||
} else if self.is_straight_line() {
|
||||
return vec![ArcOrLineSegment::Line(LineSegment {
|
||||
from: self.from,
|
||||
to: self.to,
|
||||
})];
|
||||
} else if (self.radii.x.abs() - self.radii.y.abs()).abs() < S::EPSILON {
|
||||
return vec![ArcOrLineSegment::Arc(*self)];
|
||||
}
|
||||
|
||||
let self_arc = self.to_arc();
|
||||
if let Some(svg_arc) = arc_from_endpoints_and_tangents(
|
||||
self.from,
|
||||
self_arc.sample_tangent(S::ZERO),
|
||||
self.to,
|
||||
self_arc.sample_tangent(S::ONE),
|
||||
)
|
||||
.filter(|approx_svg_arc| {
|
||||
let approx_arc = approx_svg_arc.to_arc();
|
||||
let mut max_deviation = S::ZERO;
|
||||
// TODO: find a better way to check tolerance
|
||||
// Ideally: derivative of |f(x) - g(x)| and look at 0 crossings
|
||||
for i in 1..20 {
|
||||
let t = S::from(i).unwrap() / S::from(20).unwrap();
|
||||
max_deviation =
|
||||
max_deviation.max((approx_arc.sample(t) - self_arc.sample(t)).length());
|
||||
}
|
||||
max_deviation < tolerance
|
||||
}) {
|
||||
vec![ArcOrLineSegment::Arc(svg_arc)]
|
||||
} else {
|
||||
let (left, right) = self_arc.split(S::HALF);
|
||||
let mut acc = FlattenWithArcs::flattened(&to_svg_arc(left), tolerance);
|
||||
acc.append(&mut FlattenWithArcs::flattened(
|
||||
&to_svg_arc(right),
|
||||
tolerance,
|
||||
));
|
||||
acc
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Sanity holdover until https://github.com/nical/lyon/pull/693 is merged
|
||||
pub fn to_svg_arc<S: Scalar>(arc: Arc<S>) -> SvgArc<S> {
|
||||
let from = arc.sample(S::ZERO);
|
||||
let to = arc.sample(S::ONE);
|
||||
let flags = ArcFlags {
|
||||
sweep: arc.sweep_angle.get() >= S::ZERO,
|
||||
large_arc: S::abs(arc.sweep_angle.get()) >= S::PI(),
|
||||
};
|
||||
SvgArc {
|
||||
from,
|
||||
to,
|
||||
radii: arc.radii,
|
||||
x_rotation: arc.x_rotation,
|
||||
flags,
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Transformed<S> {
|
||||
fn transformed(&self, transform: &Transform<S>) -> Self;
|
||||
}
|
||||
|
||||
impl<S: Scalar> Transformed<S> for SvgArc<S> {
|
||||
/// A lot of the math here is heavily borrowed from [Vitaly Putzin's svgpath](https://github.com/fontello/svgpath).
|
||||
///
|
||||
/// The code is Rust-ified with only one or two changes, but I plan to understand the math here and
|
||||
/// merge changes upstream to lyon-geom.
|
||||
fn transformed(&self, transform: &Transform<S>) -> Self {
|
||||
let from = transform.transform_point(self.from);
|
||||
let to = transform.transform_point(self.to);
|
||||
|
||||
// Translation does not affect rotation, radii, or flags
|
||||
let [a, b, c, d, _tx, _ty] = transform.to_array();
|
||||
let (x_rotation, radii) = {
|
||||
let (sin, cos) = self.x_rotation.sin_cos();
|
||||
|
||||
// Radii are axis-aligned -- rotate & transform
|
||||
let ma = [
|
||||
self.radii.x * (a * cos + c * sin),
|
||||
self.radii.x * (b * cos + d * sin),
|
||||
self.radii.y * (-a * sin + c * cos),
|
||||
self.radii.y * (-b * sin + d * cos),
|
||||
];
|
||||
|
||||
// ma * transpose(ma) = [ J L ]
|
||||
// [ L K ]
|
||||
// L is calculated later (if the image is not a circle)
|
||||
let J = ma[0].powi(2) + ma[2].powi(2);
|
||||
let K = ma[1].powi(2) + ma[3].powi(2);
|
||||
|
||||
// the discriminant of the characteristic polynomial of ma * transpose(ma)
|
||||
let D = ((ma[0] - ma[3]).powi(2) + (ma[2] + ma[1]).powi(2))
|
||||
* ((ma[0] + ma[3]).powi(2) + (ma[2] - ma[1]).powi(2));
|
||||
|
||||
// the "mean eigenvalue"
|
||||
let JK = (J + K) / S::TWO;
|
||||
|
||||
// check if the image is (almost) a circle
|
||||
if D < S::EPSILON * JK {
|
||||
// if it is
|
||||
(Angle::zero(), Vector::splat(JK.sqrt()))
|
||||
} else {
|
||||
// if it is not a circle
|
||||
let L = ma[0] * ma[1] + ma[2] * ma[3];
|
||||
|
||||
let D = D.sqrt();
|
||||
|
||||
// {l1,l2} = the two eigen values of ma * transpose(ma)
|
||||
let l1 = JK + D / S::TWO;
|
||||
let l2 = JK - D / S::TWO;
|
||||
// the x - axis - rotation angle is the argument of the l1 - eigenvector
|
||||
let ax = if L.abs() < S::EPSILON && (l1 - K).abs() < S::EPSILON {
|
||||
Angle::frac_pi_2()
|
||||
} else {
|
||||
Angle::radians(
|
||||
(if L.abs() > (l1 - K).abs() {
|
||||
(l1 - J) / L
|
||||
} else {
|
||||
L / (l1 - K)
|
||||
})
|
||||
.atan(),
|
||||
)
|
||||
};
|
||||
(ax, Vector::from([l1.sqrt(), l2.sqrt()]))
|
||||
}
|
||||
};
|
||||
// A mirror transform causes this flag to be flipped
|
||||
let invert_sweep = { (a * d) - (b * c) < S::ZERO };
|
||||
let flags = ArcFlags {
|
||||
sweep: if invert_sweep {
|
||||
!self.flags.sweep
|
||||
} else {
|
||||
self.flags.sweep
|
||||
},
|
||||
large_arc: self.flags.large_arc,
|
||||
};
|
||||
Self {
|
||||
from,
|
||||
to,
|
||||
radii,
|
||||
x_rotation,
|
||||
flags,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use cairo::{Context, SvgSurface};
|
||||
use lyon_geom::{point, vector, CubicBezierSegment, Point, Vector};
|
||||
use std::path::PathBuf;
|
||||
use svgtypes::PathParser;
|
||||
|
||||
use crate::arc::{ArcOrLineSegment, FlattenWithArcs};
|
||||
|
||||
#[test]
|
||||
fn flatten_returns_expected_arcs() {
|
||||
const PATH: &'static str = "M 8.0549,11.9023
|
||||
c
|
||||
0.13447,1.69916 8.85753,-5.917903 7.35159,-6.170957
|
||||
z";
|
||||
let mut surf =
|
||||
SvgSurface::new(128., 128., Some(PathBuf::from("approx_circle.svg"))).unwrap();
|
||||
surf.set_document_unit(cairo::SvgUnit::Mm);
|
||||
let ctx = Context::new(&surf).unwrap();
|
||||
ctx.set_line_width(0.2);
|
||||
|
||||
let mut current_position = Point::zero();
|
||||
|
||||
let mut acc = 0;
|
||||
|
||||
for path in PathParser::from(PATH) {
|
||||
use svgtypes::PathSegment::*;
|
||||
match path.unwrap() {
|
||||
MoveTo { x, y, abs } => {
|
||||
if abs {
|
||||
ctx.move_to(x, y);
|
||||
current_position = point(x, y);
|
||||
} else {
|
||||
ctx.rel_move_to(x, y);
|
||||
current_position += vector(x, y);
|
||||
}
|
||||
}
|
||||
LineTo { x, y, abs } => {
|
||||
if abs {
|
||||
ctx.line_to(x, y);
|
||||
current_position = point(x, y);
|
||||
} else {
|
||||
ctx.rel_line_to(x, y);
|
||||
current_position += vector(x, y);
|
||||
}
|
||||
}
|
||||
ClosePath { .. } => ctx.close_path(),
|
||||
CurveTo {
|
||||
x1,
|
||||
y1,
|
||||
x2,
|
||||
y2,
|
||||
x,
|
||||
y,
|
||||
abs,
|
||||
} => {
|
||||
ctx.set_dash(&[], 0.);
|
||||
match acc {
|
||||
0 => ctx.set_source_rgb(1., 0., 0.),
|
||||
1 => ctx.set_source_rgb(0., 1., 0.),
|
||||
2 => ctx.set_source_rgb(0., 0., 1.),
|
||||
3 => ctx.set_source_rgb(0., 0., 0.),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
let curve = CubicBezierSegment {
|
||||
from: current_position,
|
||||
ctrl1: (vector(x1, y1)
|
||||
+ if !abs {
|
||||
current_position.to_vector()
|
||||
} else {
|
||||
Vector::zero()
|
||||
})
|
||||
.to_point(),
|
||||
ctrl2: (vector(x2, y2)
|
||||
+ if !abs {
|
||||
current_position.to_vector()
|
||||
} else {
|
||||
Vector::zero()
|
||||
})
|
||||
.to_point(),
|
||||
to: (vector(x, y)
|
||||
+ if !abs {
|
||||
current_position.to_vector()
|
||||
} else {
|
||||
Vector::zero()
|
||||
})
|
||||
.to_point(),
|
||||
};
|
||||
for segment in FlattenWithArcs::flattened(&curve, 0.02) {
|
||||
match segment {
|
||||
ArcOrLineSegment::Arc(svg_arc) => {
|
||||
let arc = svg_arc.to_arc();
|
||||
if svg_arc.flags.sweep {
|
||||
ctx.arc(
|
||||
arc.center.x,
|
||||
arc.center.y,
|
||||
arc.radii.x,
|
||||
arc.start_angle.radians,
|
||||
(arc.start_angle + arc.sweep_angle).radians,
|
||||
)
|
||||
} else {
|
||||
ctx.arc_negative(
|
||||
arc.center.x,
|
||||
arc.center.y,
|
||||
arc.radii.x,
|
||||
arc.start_angle.radians,
|
||||
(arc.start_angle + arc.sweep_angle).radians,
|
||||
)
|
||||
}
|
||||
}
|
||||
ArcOrLineSegment::Line(line) => ctx.line_to(line.to.x, line.to.y),
|
||||
}
|
||||
}
|
||||
|
||||
ctx.stroke().unwrap();
|
||||
|
||||
current_position = curve.to;
|
||||
ctx.set_dash(&[0.1], 0.);
|
||||
ctx.move_to(curve.from.x, curve.from.y);
|
||||
ctx.curve_to(
|
||||
curve.ctrl1.x,
|
||||
curve.ctrl1.y,
|
||||
curve.ctrl2.x,
|
||||
curve.ctrl2.y,
|
||||
curve.to.x,
|
||||
curve.to.y,
|
||||
);
|
||||
ctx.stroke().unwrap();
|
||||
acc += 1;
|
||||
}
|
||||
other => unimplemented!("{:?}", other),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,145 @@
|
||||
G21
|
||||
G90;svg#svg8 > g#layer1 > path
|
||||
G0 X1 Y9
|
||||
G1 X1.108112220817491 Y8.887981529182511 F300
|
||||
G1 X1.2153787137015988 Y8.768996286298401 F300
|
||||
G2 X1.4239916039361313 Y8.51350839606387 R5.11981430218831 F300
|
||||
G2 X1.7938566201357353 Y7.956143379864265 R4.835393156461376 F300
|
||||
G2 X1.9415770991666748 Y7.6677979008333255 R3.8134637231185913 F300
|
||||
G1 X2.0031742836480873 Y7.524169466351913 F300
|
||||
G1 X2.0554684608622846 Y7.382031539137715 F300
|
||||
G1 X2.0976139028758833 Y7.242229847124117 F300
|
||||
G1 X2.128764881755499 Y7.105610118244501 F300
|
||||
G1 X2.148075669567749 Y6.973018080432251 F300
|
||||
G2 X2.1547005383792515 Y6.8452994616207485 R1.2759926937417487 F300
|
||||
G2 X2.1397549853513067 Y6.668858900493001 R1.0008154063555685 F300
|
||||
G2 X2.0931488160479113 Y6.50733213111376 R0.7841189700439164 F300
|
||||
G2 X2.0122277651397247 Y6.363373418812364 R0.6809578083609312 F300
|
||||
G2 X1.8943375672974065 Y6.239637028918155 R0.6907022975872513 F300
|
||||
G1 X1.8206995803605288 Y6.186181662789164 F300
|
||||
G1 X1.7368239571916155 Y6.138777226760473 F300
|
||||
G1 X1.6423789146244991 Y6.097755503998249 F300
|
||||
G1 X1.5370326694930114 Y6.06344827766866 F300
|
||||
G1 X1.4204534386309855 Y6.036187330937873 F300
|
||||
G1 X1.2923094388722531 Y6.016304446972055 F300
|
||||
G1 X1.1522688870506472 Y6.004131408937375 F300
|
||||
G1 X1 Y6 F300;svg#svg8 > g#layer1 > path
|
||||
G0 X1 Y5
|
||||
G3 X1.1025 Y4.9125 R0.81697048500483 F300
|
||||
G3 X1.2100000000000002 Y4.8500000000000005 R0.6432262761687367 F300
|
||||
G3 X1.4400000000000004 Y4.800000000000001 R0.5559102124679794 F300
|
||||
G3 X1.5625000000000004 Y4.8125 R0.6248177401751354 F300
|
||||
G3 X1.6900000000000004 Y4.8500000000000005 R0.7837286053113949 F300
|
||||
G3 X1.8225000000000007 Y4.9125 R1.0478526379133146 F300
|
||||
G1 X1.8906250000000004 Y4.953125 F300
|
||||
G1 X1.9600000000000004 Y5 F300
|
||||
G1 X2.0306250000000006 Y5.053125 F300
|
||||
G1 X2.1025000000000005 Y5.1125 F300
|
||||
G1 X2.175625 Y5.178125 F300
|
||||
G1 X2.2500000000000004 Y5.25 F300
|
||||
G1 X2.3256250000000005 Y5.328125 F300
|
||||
G1 X2.4025000000000007 Y5.4125 F300
|
||||
G1 X2.4806250000000007 Y5.503125 F300
|
||||
G1 X2.5600000000000005 Y5.6000000000000005 F300
|
||||
G1 X2.7225 Y5.8125 F300
|
||||
G1 X2.89 Y6.050000000000001 F300
|
||||
G1 X3.0625 Y6.3125 F300
|
||||
G1 X3.24 Y6.6 F300
|
||||
G1 X3.4225000000000003 Y6.9125 F300
|
||||
G1 X3.61 Y7.25 F300
|
||||
G1 X3.8025 Y7.612500000000001 F300
|
||||
G1 X4 Y8 F300;svg#svg8 > g#layer1 > path
|
||||
G0 X7 Y9
|
||||
G2 X4 Y7 R2 F300;svg#svg8 > g#layer1 > path
|
||||
G0 X1 Y4
|
||||
G3 X1.3978250325985413 Y3.968156676904629 R14.876279155824578 F300
|
||||
G3 X1.8079854783033578 Y3.946104968199289 R16.01024971452823 F300
|
||||
G3 X2.6441213528766796 Y3.932607474577383 R16.909229019806858 F300
|
||||
G3 X3.0621524614580276 Y3.9412866185390385 R16.902307637846288 F300
|
||||
G3 X3.474762735928376 Y3.9601731711554446 R16.28877876962794 F300
|
||||
G3 X3.8778904316608083 Y3.9890778449897057 R15.268674575836199 F300
|
||||
G3 X4.267739066726786 Y4.0277250178448885 R13.912776714019424 F300
|
||||
G3 X4.456063577509985 Y4.050532552971807 R12.739268728330957 F300
|
||||
G3 X4.63949362667036 Y4.075589152050236 R11.886079594871317 F300
|
||||
G3 X4.9912723595593835 Y4.132404125106506 R10.514058134441601 F300
|
||||
G3 X5.158404895076147 Y4.163979552996096 R9.124219221791998 F300
|
||||
G3 X5.319615850363981 Y4.197668785821917 R8.191555678247077 F300
|
||||
G3 X5.473047754155922 Y4.233066714906146 R7.337969988742633 F300
|
||||
G3 X5.61937862477731 Y4.270296440546117 R6.491816535421121 F300
|
||||
G3 X5.7594343548684455 Y4.309615721254547 R5.617604058887768 F300
|
||||
G3 X5.8915689090734364 Y4.350612794689171 R4.822901450495766 F300
|
||||
G3 X6.015159340298373 Y4.3930787556347495 R4.103817344924035 F300
|
||||
G3 X6.130250150760052 Y4.437012246178796 R3.44384482377765 F300
|
||||
G3 X6.236127588090238 Y4.48211064322029 R2.846139959892035 F300
|
||||
G3 X6.333048170507615 Y4.528450609158775 R2.3152352933048315 F300
|
||||
G3 X6.4208476002165105 Y4.575960377763483 R1.852483263205878 F300
|
||||
G3 X6.499231927533419 Y4.624488389580274 R1.4581571431397116 F300
|
||||
G3 X6.5681780722511505 Y4.674046907498437 R1.1299709145518102 F300
|
||||
G3 X6.6273047901063284 Y4.724392183641678 R0.8643192961566583 F300
|
||||
G3 X6.676477557058046 Y4.775412218763866 R0.6564208388485623 F300
|
||||
G3 X6.71556575202882 Y4.826974546197227 R0.5001673711308741 F300
|
||||
G3 X6.763123778129433 Y4.931181762950229 R0.350386491482661 F300
|
||||
G3 X6.769570039513327 Y5.036030574790918 R0.2731563882430039 F300
|
||||
G3 X6.734853500522512 Y5.140538119506337 R0.3127589306728062 F300
|
||||
G3 X6.702124660145641 Y5.192377872230199 R0.42576793104995136 F300
|
||||
G3 X6.659260252238352 Y5.243740866196475 R0.5529909118803078 F300
|
||||
G3 X6.606369810412423 Y5.294504520034969 R0.7277547047047377 F300
|
||||
G3 X6.543572649892903 Y5.344557674458401 R0.9564889710367651 F300
|
||||
G3 X6.471071971365293 Y5.393746987681996 R1.2449378228703627 F300
|
||||
G3 X6.389003780317571 Y5.441987645437967 R1.597798863130081 F300
|
||||
G3 X6.297869780912585 Y5.489015899982382 R2.0177297473684925 F300
|
||||
G3 X6.197643386983442 Y5.534873412599875 R2.505678774355954 F300
|
||||
G3 X6.088496927021365 Y5.579476569875775 R3.0612941796334256 F300
|
||||
G3 X5.970749082451981 Y5.622690761009283 R3.6808379091910886 F300
|
||||
G3 X5.844081790059699 Y5.664601230818244 R4.360700929904796 F300
|
||||
G3 X5.709324860866246 Y5.70490397007849 R5.102368612887797 F300
|
||||
G3 X5.567733146370779 Y5.743261946530389 R5.934854156765002 F300
|
||||
G3 X5.418814245543702 Y5.779844664709513 R6.798940676792501 F300
|
||||
G3 X5.261537124667706 Y5.814858597710807 R7.631496009070628 F300
|
||||
G3 X5.097548832796083 Y5.847892236765922 R8.528687532226238 F300
|
||||
G3 X4.9276849905723505 Y5.878789845134821 R9.47189940399368 F300
|
||||
G3 X4.751951029840501 Y5.9075625483307235 R10.4133131491631 F300
|
||||
G3 X4.386110365917731 Y5.958272783471742 R11.758269645230168 F300
|
||||
G3 X4.196158507416505 Y5.980211912487405 R13.046962723496428 F300
|
||||
G3 X4.002046461235404 Y5.999807994943473 R13.831352786873643 F300;svg#svg8 > g#layer1 > path
|
||||
G0 X1 Y2
|
||||
G3 X2.7071067811865475 Y1.2928932188134523 R1 F300
|
||||
G3 X2.0000000000000004 Y3 R1 F300;svg#svg8 > g#layer1 > path
|
||||
G0 X7 Y2
|
||||
G3 X7.154762363949493 Y2.109124437803701 R1.3064226822292095 F300
|
||||
G3 X7.282116680361381 Y2.2321077959060944 R1.0856892593761847 F300
|
||||
G3 X7.380081728935076 Y2.366714137608437 R0.9044562895491561 F300
|
||||
G3 X7.446435873469992 Y2.5098930035038536 R0.7704193428710345 F300
|
||||
G3 X7.479017753360532 Y2.809177694882109 R0.6803427665568468 F300
|
||||
G3 X7.444418384862246 Y2.9583778658834623 R0.7026557460446882 F300
|
||||
G3 X7.3766799299740935 Y3.1027417096732983 R0.7922831187696943 F300
|
||||
G3 X7.277389351167718 Y3.2389398933696354 R0.9367339275418858 F300
|
||||
G3 X7.148774650157433 Y3.3639502152420433 R1.125271732619803 F300
|
||||
G3 X6.993673148291746 Y3.474987144669766 R1.3540462899654855 F300
|
||||
G3 X6.815657407470125 Y3.5694700860997925 R1.5910816869891395 F300
|
||||
G3 X6.619051181089784 Y3.6451637975031086 R1.8311381180551767 F300
|
||||
G3 X6.408070496157389 Y3.700498981631148 R2.0295147528189914 F300
|
||||
G3 X5.961879008015639 Y3.7455746745355167 R2.20667175175377 F300
|
||||
G3 X5.51774327794617 Y3.7004924716052043 R2.177719386997694 F300
|
||||
G3 X5.309067761171235 Y3.6451184265986116 R1.9659448824172234 F300
|
||||
G3 X5.115776986833164 Y3.5694123856790956 R1.7512568735632301 F300
|
||||
G3 X4.941808710313139 Y3.4748620651716124 R1.509194160282913 F300
|
||||
G3 X4.791506394813442 Y3.3637428366487176 R1.2687625765933719 F300
|
||||
G3 X4.668454022834454 Y3.2387839626056327 R1.0557113303482242 F300
|
||||
G3 X4.575183183400513 Y3.1026532892406093 R0.8805630550146627 F300
|
||||
G3 X4.513751798468469 Y2.9582561971686174 R0.7547083052304115 F300
|
||||
G3 X4.4856847179921875 Y2.80902758022418 R0.6846718778044363 F300
|
||||
G3 X4.531388123426751 Y2.509739067236653 R0.6913082436962433 F300
|
||||
G3 X4.604045213168647 Y2.3665275739817453 R0.8110589553538909 F300
|
||||
G3 X4.707904549977265 Y2.231952183082983 R0.9631939268966888 F300
|
||||
G3 X4.840756715708391 Y2.108903718054256 R1.15692448047627 F300
|
||||
G3 X4.999519011965418 Y2.0002834466817996 R1.3906772528175693 F300
|
||||
G3 X5.221523434813825 Y1.8914584411737705 R1.6541860008828217 F300
|
||||
G3 X5.468658098289327 Y1.8111179864415283 R1.929021932467884 F300
|
||||
G3 X5.732969365528165 Y1.7618093869229279 R2.132487933789956 F300
|
||||
G3 X6.005616291542055 Y1.7451939792884783 R2.236659915629062 F300
|
||||
G3 X6.277559043718761 Y1.761826851152393 R2.218645912284593 F300
|
||||
G3 X6.539734110392067 Y1.811157851163688 R2.082787785187509 F300
|
||||
G3 X6.7834314749579985 Y1.891549416416874 R1.8563026877496185 F300
|
||||
G3 X7.00054219152339 Y2.000327963727709 R1.5688254782733724 F300
|
||||
M2
|
After Width: | Height: | Size: 2.7 KiB |
Loading…
Reference in new issue