2016-07-06 04:51:11 +00:00
# External dependencies
from __future__ import division , absolute_import , print_function
2021-09-23 10:52:16 +00:00
import os
2021-11-02 01:05:23 +00:00
from sys import version_info
2016-07-06 04:51:11 +00:00
import unittest
from math import sqrt , pi
2017-02-21 06:57:59 +00:00
from operator import itemgetter
2018-06-09 05:13:18 +00:00
import numpy as np
2018-11-05 04:55:17 +00:00
import random
2021-06-28 05:30:56 +00:00
import warnings
2016-07-06 04:51:11 +00:00
# Internal dependencies
2023-02-04 00:46:23 +00:00
from svgpathtools import (
Line , QuadraticBezier , CubicBezier , Arc , Path , poly2bez , path_encloses_pt ,
bpoints2bezier , closest_point_in_path , farthest_point_in_path ,
is_bezier_segment , is_bezier_path , parse_path
)
from svgpathtools . path import bezier_radialrange
2016-07-06 04:51:11 +00:00
2017-02-21 01:21:53 +00:00
# An important note for those doing any debugging:
# ------------------------------------------------
2016-07-06 04:51:11 +00:00
# Most of these test points are not calculated separately, as that would
# take too long and be too error prone. Instead the curves have been verified
# to be correct visually with the disvg() function.
2021-06-28 05:30:56 +00:00
RUN_SLOW_TESTS = False
TOL = 1e-4 # default for tests that don't specify a `delta` or `places`
2018-11-05 04:55:17 +00:00
def random_line ( ) :
x = ( random . random ( ) - 0.5 ) * 2000
y = ( random . random ( ) - 0.5 ) * 2000
start = complex ( x , y )
x = ( random . random ( ) - 0.5 ) * 2000
y = ( random . random ( ) - 0.5 ) * 2000
end = complex ( x , y )
return Line ( start , end )
def random_arc ( ) :
x = ( random . random ( ) - 0.5 ) * 2000
y = ( random . random ( ) - 0.5 ) * 2000
start = complex ( x , y )
x = ( random . random ( ) - 0.5 ) * 2000
y = ( random . random ( ) - 0.5 ) * 2000
end = complex ( x , y )
x = ( random . random ( ) - 0.5 ) * 2000
y = ( random . random ( ) - 0.5 ) * 2000
radius = complex ( x , y )
large_arc = random . choice ( [ True , False ] )
sweep = random . choice ( [ True , False ] )
return Arc ( start = start , radius = radius , rotation = 0.0 , large_arc = large_arc , sweep = sweep , end = end )
2021-06-28 05:30:56 +00:00
def assert_intersections ( test_case , a_seg , b_seg , intersections , count , msg = None , tol = 1e-4 ) :
if count is not None :
test_case . assertTrue ( len ( intersections ) == count , msg = msg )
2018-11-05 04:55:17 +00:00
for i in intersections :
2021-06-28 05:30:56 +00:00
test_case . assertTrue ( i [ 0 ] > = 0.0 , msg = msg )
test_case . assertTrue ( i [ 0 ] < = 1.0 , msg = msg )
test_case . assertTrue ( i [ 1 ] > = 0.0 , msg = msg )
test_case . assertTrue ( i [ 1 ] < = 1.0 , msg = msg )
test_case . assertAlmostEqual ( a_seg . point ( i [ 0 ] ) , b_seg . point ( i [ 1 ] ) , msg = msg , delta = tol )
2018-11-05 04:55:17 +00:00
2016-07-06 04:51:11 +00:00
class LineTest ( unittest . TestCase ) :
def test_lines ( self ) :
# These points are calculated, and not just regression tests.
line1 = Line ( 0 j , 400 + 0 j )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( line1 . point ( 0 ) , 0 j , delta = TOL )
self . assertAlmostEqual ( line1 . point ( 0.3 ) , ( 120 + 0 j ) , delta = TOL )
self . assertAlmostEqual ( line1 . point ( 0.5 ) , ( 200 + 0 j ) , delta = TOL )
self . assertAlmostEqual ( line1 . point ( 0.9 ) , ( 360 + 0 j ) , delta = TOL )
self . assertAlmostEqual ( line1 . point ( 1 ) , ( 400 + 0 j ) , delta = TOL )
self . assertAlmostEqual ( line1 . length ( ) , 400 , delta = TOL )
2016-07-06 04:51:11 +00:00
line2 = Line ( 400 + 0 j , 400 + 300 j )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( line2 . point ( 0 ) , ( 400 + 0 j ) , delta = TOL )
self . assertAlmostEqual ( line2 . point ( 0.3 ) , ( 400 + 90 j ) , delta = TOL )
self . assertAlmostEqual ( line2 . point ( 0.5 ) , ( 400 + 150 j ) , delta = TOL )
self . assertAlmostEqual ( line2 . point ( 0.9 ) , ( 400 + 270 j ) , delta = TOL )
self . assertAlmostEqual ( line2 . point ( 1 ) , ( 400 + 300 j ) , delta = TOL )
self . assertAlmostEqual ( line2 . length ( ) , 300 , delta = TOL )
2016-07-06 04:51:11 +00:00
line3 = Line ( 400 + 300 j , 0 j )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( line3 . point ( 0 ) , ( 400 + 300 j ) , delta = TOL )
self . assertAlmostEqual ( line3 . point ( 0.3 ) , ( 280 + 210 j ) , delta = TOL )
self . assertAlmostEqual ( line3 . point ( 0.5 ) , ( 200 + 150 j ) , delta = TOL )
self . assertAlmostEqual ( line3 . point ( 0.9 ) , ( 40 + 30 j ) , delta = TOL )
self . assertAlmostEqual ( line3 . point ( 1 ) , 0 j , delta = TOL )
self . assertAlmostEqual ( line3 . length ( ) , 500 , delta = TOL )
2016-07-06 04:51:11 +00:00
def test_equality ( self ) :
# This is to test the __eq__ and __ne__ methods, so we can't use
# assertEqual and assertNotEqual
line = Line ( 0 j , 400 + 0 j )
2018-05-23 02:34:56 +00:00
cubic = CubicBezier ( 600 + 500 j , 600 + 350 j , 900 + 650 j , 900 + 500 j )
2016-07-06 04:51:11 +00:00
self . assertTrue ( line == Line ( 0 , 400 ) )
self . assertTrue ( line != Line ( 100 , 400 ) )
self . assertFalse ( line == str ( line ) )
self . assertTrue ( line != str ( line ) )
2018-05-23 02:34:56 +00:00
self . assertFalse ( cubic == line )
2016-07-06 04:51:11 +00:00
2018-11-05 04:55:17 +00:00
def test_point_to_t ( self ) :
l = Line ( start = ( 0 + 0 j ) , end = ( 0 + 10 j ) )
self . assertEqual ( l . point_to_t ( 0 + 0 j ) , 0.0 )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( l . point_to_t ( 0 + 5 j ) , 0.5 , delta = TOL )
2018-11-05 04:55:17 +00:00
self . assertEqual ( l . point_to_t ( 0 + 10 j ) , 1.0 )
self . assertIsNone ( l . point_to_t ( 1 + 0 j ) )
self . assertIsNone ( l . point_to_t ( 0 - 1 j ) )
self . assertIsNone ( l . point_to_t ( 0 + 11 j ) )
l = Line ( start = ( 0 + 0 j ) , end = ( 10 + 10 j ) )
self . assertEqual ( l . point_to_t ( 0 + 0 j ) , 0.0 )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( l . point_to_t ( 5 + 5 j ) , 0.5 , delta = TOL )
2018-11-05 04:55:17 +00:00
self . assertEqual ( l . point_to_t ( 10 + 10 j ) , 1.0 )
self . assertIsNone ( l . point_to_t ( 1 + 0 j ) )
self . assertIsNone ( l . point_to_t ( 0 - 1 j ) )
self . assertIsNone ( l . point_to_t ( 0 + 11 j ) )
self . assertIsNone ( l . point_to_t ( 10.001 + 10.001 j ) )
self . assertIsNone ( l . point_to_t ( - 0.001 - 0.001 j ) )
l = Line ( start = ( 0 + 0 j ) , end = ( 10 + 0 j ) )
self . assertEqual ( l . point_to_t ( 0 + 0 j ) , 0.0 )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( l . point_to_t ( 5 + 0 j ) , 0.5 , delta = TOL )
2018-11-05 04:55:17 +00:00
self . assertEqual ( l . point_to_t ( 10 + 0 j ) , 1.0 )
self . assertIsNone ( l . point_to_t ( 0 + 1 j ) )
self . assertIsNone ( l . point_to_t ( 0 - 1 j ) )
self . assertIsNone ( l . point_to_t ( 0 + 11 j ) )
self . assertIsNone ( l . point_to_t ( 10.001 + 0 j ) )
self . assertIsNone ( l . point_to_t ( - 0.001 - 0 j ) )
l = Line ( start = ( - 2 - 1 j ) , end = ( 11 - 20 j ) )
self . assertEqual ( l . point_to_t ( - 2 - 1 j ) , 0.0 )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( l . point_to_t ( 4.5 - 10.5 j ) , 0.5 , delta = TOL )
2018-11-05 04:55:17 +00:00
self . assertEqual ( l . point_to_t ( 11 - 20 j ) , 1.0 )
self . assertIsNone ( l . point_to_t ( 0 + 1 j ) )
self . assertIsNone ( l . point_to_t ( 0 - 1 j ) )
self . assertIsNone ( l . point_to_t ( 0 + 11 j ) )
self . assertIsNone ( l . point_to_t ( 10.001 + 0 j ) )
self . assertIsNone ( l . point_to_t ( - 0.001 - 0 j ) )
l = Line ( start = ( 40.234 - 32.613 j ) , end = ( 12.7 - 32.613 j ) )
self . assertEqual ( l . point_to_t ( 40.234 - 32.613 j ) , 0.0 )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( l . point_to_t ( 33.3505 - 32.613 j ) , 0.25 , delta = TOL )
self . assertAlmostEqual ( l . point_to_t ( 26.467 - 32.613 j ) , 0.50 , delta = TOL )
self . assertAlmostEqual ( l . point_to_t ( 19.5835 - 32.613 j ) , 0.75 , delta = TOL )
2018-11-05 04:55:17 +00:00
self . assertEqual ( l . point_to_t ( 12.7 - 32.613 j ) , 1.0 )
self . assertIsNone ( l . point_to_t ( 40.25 - 32.613 j ) )
self . assertIsNone ( l . point_to_t ( 12.65 - 32.613 j ) )
self . assertIsNone ( l . point_to_t ( 11 - 20 j ) )
self . assertIsNone ( l . point_to_t ( 0 + 1 j ) )
self . assertIsNone ( l . point_to_t ( 0 - 1 j ) )
self . assertIsNone ( l . point_to_t ( 0 + 11 j ) )
self . assertIsNone ( l . point_to_t ( 10.001 + 0 j ) )
self . assertIsNone ( l . point_to_t ( - 0.001 - 0 j ) )
random . seed ( )
for line_index in range ( 100 ) :
l = random_line ( )
for t_index in range ( 100 ) :
orig_t = random . random ( )
p = l . point ( orig_t )
computed_t = l . point_to_t ( p )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( orig_t , computed_t , delta = TOL )
2018-11-05 04:55:17 +00:00
2020-07-02 05:31:57 +00:00
def test_radialrange ( self ) :
def crand ( ) :
return 100 * ( np . random . rand ( ) + np . random . rand ( ) * 1 j )
for _ in range ( 100 ) :
z = crand ( )
l = Line ( crand ( ) , crand ( ) )
( min_da , min_ta ) , ( max_da , max_ta ) = l . radialrange ( z )
( min_db , min_tb ) , ( max_db , max_tb ) = bezier_radialrange ( l , z )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( min_da , min_db , delta = TOL )
self . assertAlmostEqual ( min_ta , min_tb , delta = TOL )
self . assertAlmostEqual ( max_da , max_db , delta = TOL )
self . assertAlmostEqual ( max_ta , max_tb , delta = TOL )
2018-11-05 04:55:17 +00:00
2016-07-06 04:51:11 +00:00
class CubicBezierTest ( unittest . TestCase ) :
def test_approx_circle ( self ) :
""" This is a approximate circle drawn in Inkscape """
cub1 = CubicBezier (
complex ( 0 , 0 ) ,
complex ( 0 , 109.66797 ) ,
complex ( - 88.90345 , 198.57142 ) ,
complex ( - 198.57142 , 198.57142 )
)
2021-06-28 05:30:56 +00:00
cub1_tests = [
( 0 , 0 j ) ,
( 0.1 , ( - 2.59896457 + 32.20931647 j ) ) ,
( 0.2 , ( - 10.12330256 + 62.76392816 j ) ) ,
( 0.3 , ( - 22.16418039 + 91.25500149 j ) ) ,
( 0.4 , ( - 38.31276448 + 117.27370288 j ) ) ,
( 0.5 , ( - 58.16022125 + 140.41119875 j ) ) ,
( 0.6 , ( - 81.29771712 + 160.25865552 j ) ) ,
( 0.7 , ( - 107.31641851 + 176.40723961 j ) ) ,
( 0.8 , ( - 135.80749184 + 188.44811744 j ) ) ,
( 0.9 , ( - 166.36210353 + 195.97245543 j ) ) ,
( 1 , ( - 198.57142 + 198.57142 j ) ) ,
]
2016-07-06 04:51:11 +00:00
cub2 = CubicBezier (
complex ( - 198.57142 , 198.57142 ) ,
complex ( - 109.66797 - 198.57142 , 0 + 198.57142 ) ,
complex ( - 198.57143 - 198.57142 , - 88.90345 + 198.57142 ) ,
complex ( - 198.57143 - 198.57142 , 0 ) ,
)
2021-06-28 05:30:56 +00:00
cub2_tests = [
( 0 , ( - 198.57142 + 198.57142 j ) ) ,
( 0.1 , ( - 230.78073675 + 195.97245543 j ) ) ,
( 0.2 , ( - 261.3353492 + 188.44811744 j ) ) ,
( 0.3 , ( - 289.82642365 + 176.40723961 j ) ) ,
( 0.4 , ( - 315.8451264 + 160.25865552 j ) ) ,
( 0.5 , ( - 338.98262375 + 140.41119875 j ) ) ,
( 0.6 , ( - 358.830082 + 117.27370288 j ) ) ,
( 0.7 , ( - 374.97866745 + 91.25500149 j ) ) ,
( 0.8 , ( - 387.0195464 + 62.76392816 j ) ) ,
( 0.9 , ( - 394.54388515 + 32.20931647 j ) ) ,
( 1 , ( - 397.14285 + 0 j ) ) ,
]
2016-07-06 04:51:11 +00:00
cub3 = CubicBezier (
complex ( - 198.57143 - 198.57142 , 0 ) ,
complex ( 0 - 198.57143 - 198.57142 , - 109.66797 ) ,
complex ( 88.90346 - 198.57143 - 198.57142 , - 198.57143 ) ,
complex ( - 198.57142 , - 198.57143 )
)
2021-06-28 05:30:56 +00:00
cub3_tests = [
( 0 , ( - 397.14285 + 0 j ) ) ,
( 0.1 , ( - 394.54388515 - 32.20931675 j ) ) ,
( 0.2 , ( - 387.0195464 - 62.7639292 j ) ) ,
( 0.3 , ( - 374.97866745 - 91.25500365 j ) ) ,
( 0.4 , ( - 358.830082 - 117.2737064 j ) ) ,
( 0.5 , ( - 338.98262375 - 140.41120375 j ) ) ,
( 0.6 , ( - 315.8451264 - 160.258662 j ) ) ,
( 0.7 , ( - 289.82642365 - 176.40724745 j ) ) ,
( 0.8 , ( - 261.3353492 - 188.4481264 j ) ) ,
( 0.9 , ( - 230.78073675 - 195.97246515 j ) ) ,
( 1 , ( - 198.57142 - 198.57143 j ) ) ,
]
2016-07-06 04:51:11 +00:00
cub4 = CubicBezier (
complex ( - 198.57142 , - 198.57143 ) ,
complex ( 109.66797 - 198.57142 , 0 - 198.57143 ) ,
complex ( 0 , 88.90346 - 198.57143 ) ,
complex ( 0 , 0 ) ,
)
2021-06-28 05:30:56 +00:00
cub4_tests = [
( 0 , ( - 198.57142 - 198.57143 j ) ) ,
( 0.1 , ( - 166.36210353 - 195.97246515 j ) ) ,
( 0.2 , ( - 135.80749184 - 188.4481264 j ) ) ,
( 0.3 , ( - 107.31641851 - 176.40724745 j ) ) ,
( 0.4 , ( - 81.29771712 - 160.258662 j ) ) ,
( 0.5 , ( - 58.16022125 - 140.41120375 j ) ) ,
( 0.6 , ( - 38.31276448 - 117.2737064 j ) ) ,
( 0.7 , ( - 22.16418039 - 91.25500365 j ) ) ,
( 0.8 , ( - 10.12330256 - 62.7639292 j ) ) ,
( 0.9 , ( - 2.59896457 - 32.20931675 j ) ) ,
( 1 , 0 j ) ,
]
test_sets = [
( ' cub1 ' , cub1 , cub1_tests ) ,
( ' cub2 ' , cub2 , cub2_tests ) ,
( ' cub3 ' , cub3 , cub3_tests ) ,
( ' cub4 ' , cub4 , cub4_tests ) ,
]
tol = 1e-4
for set_name , path_segment , test_set in test_sets :
for t , expected_result in test_set :
result = path_segment . point ( t )
msg = ' {} .point( {} ) = {} | expected_result = {} ' \
' ' . format ( set_name , t , result , expected_result )
self . assertAlmostEqual ( result , expected_result , msg = msg , delta = tol )
2016-07-06 04:51:11 +00:00
def test_svg_examples ( self ) :
# M100,200 C100,100 250,100 250,200
path1 = CubicBezier ( 100 + 200 j , 100 + 100 j , 250 + 100 j , 250 + 200 j )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( path1 . point ( 0 ) , ( 100 + 200 j ) , delta = TOL )
self . assertAlmostEqual ( path1 . point ( 0.3 ) , ( 132.4 + 137 j ) , delta = TOL )
self . assertAlmostEqual ( path1 . point ( 0.5 ) , ( 175 + 125 j ) , delta = TOL )
self . assertAlmostEqual ( path1 . point ( 0.9 ) , ( 245.8 + 173 j ) , delta = TOL )
self . assertAlmostEqual ( path1 . point ( 1 ) , ( 250 + 200 j ) , delta = TOL )
2016-07-06 04:51:11 +00:00
# S400,300 400,200
path2 = CubicBezier ( 250 + 200 j , 250 + 300 j , 400 + 300 j , 400 + 200 j )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( path2 . point ( 0 ) , ( 250 + 200 j ) , delta = TOL )
self . assertAlmostEqual ( path2 . point ( 0.3 ) , ( 282.4 + 263 j ) , delta = TOL )
self . assertAlmostEqual ( path2 . point ( 0.5 ) , ( 325 + 275 j ) , delta = TOL )
self . assertAlmostEqual ( path2 . point ( 0.9 ) , ( 395.8 + 227 j ) , delta = TOL )
self . assertAlmostEqual ( path2 . point ( 1 ) , ( 400 + 200 j ) , delta = TOL )
2016-07-06 04:51:11 +00:00
# M100,200 C100,100 400,100 400,200
path3 = CubicBezier ( 100 + 200 j , 100 + 100 j , 400 + 100 j , 400 + 200 j )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( path3 . point ( 0 ) , ( 100 + 200 j ) , delta = TOL )
self . assertAlmostEqual ( path3 . point ( 0.3 ) , ( 164.8 + 137 j ) , delta = TOL )
self . assertAlmostEqual ( path3 . point ( 0.5 ) , ( 250 + 125 j ) , delta = TOL )
self . assertAlmostEqual ( path3 . point ( 0.9 ) , ( 391.6 + 173 j ) , delta = TOL )
self . assertAlmostEqual ( path3 . point ( 1 ) , ( 400 + 200 j ) , delta = TOL )
2016-07-06 04:51:11 +00:00
# M100,500 C25,400 475,400 400,500
path4 = CubicBezier ( 100 + 500 j , 25 + 400 j , 475 + 400 j , 400 + 500 j )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( path4 . point ( 0 ) , ( 100 + 500 j ) , delta = TOL )
self . assertAlmostEqual ( path4 . point ( 0.3 ) , ( 145.9 + 437 j ) , delta = TOL )
self . assertAlmostEqual ( path4 . point ( 0.5 ) , ( 250 + 425 j ) , delta = TOL )
self . assertAlmostEqual ( path4 . point ( 0.9 ) , ( 407.8 + 473 j ) , delta = TOL )
self . assertAlmostEqual ( path4 . point ( 1 ) , ( 400 + 500 j ) , delta = TOL )
2016-07-06 04:51:11 +00:00
# M100,800 C175,700 325,700 400,800
path5 = CubicBezier ( 100 + 800 j , 175 + 700 j , 325 + 700 j , 400 + 800 j )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( path5 . point ( 0 ) , ( 100 + 800 j ) , delta = TOL )
self . assertAlmostEqual ( path5 . point ( 0.3 ) , ( 183.7 + 737 j ) , delta = TOL )
self . assertAlmostEqual ( path5 . point ( 0.5 ) , ( 250 + 725 j ) , delta = TOL )
self . assertAlmostEqual ( path5 . point ( 0.9 ) , ( 375.4 + 773 j ) , delta = TOL )
self . assertAlmostEqual ( path5 . point ( 1 ) , ( 400 + 800 j ) , delta = TOL )
2016-07-06 04:51:11 +00:00
# M600,200 C675,100 975,100 900,200
path6 = CubicBezier ( 600 + 200 j , 675 + 100 j , 975 + 100 j , 900 + 200 j )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( path6 . point ( 0 ) , ( 600 + 200 j ) , delta = TOL )
self . assertAlmostEqual ( path6 . point ( 0.3 ) , ( 712.05 + 137 j ) , delta = TOL )
self . assertAlmostEqual ( path6 . point ( 0.5 ) , ( 806.25 + 125 j ) , delta = TOL )
self . assertAlmostEqual ( path6 . point ( 0.9 ) , ( 911.85 + 173 j ) , delta = TOL )
self . assertAlmostEqual ( path6 . point ( 1 ) , ( 900 + 200 j ) , delta = TOL )
2016-07-06 04:51:11 +00:00
# M600,500 C600,350 900,650 900,500
path7 = CubicBezier ( 600 + 500 j , 600 + 350 j , 900 + 650 j , 900 + 500 j )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( path7 . point ( 0 ) , ( 600 + 500 j ) , delta = TOL )
self . assertAlmostEqual ( path7 . point ( 0.3 ) , ( 664.8 + 462.2 j ) , delta = TOL )
self . assertAlmostEqual ( path7 . point ( 0.5 ) , ( 750 + 500 j ) , delta = TOL )
self . assertAlmostEqual ( path7 . point ( 0.9 ) , ( 891.6 + 532.4 j ) , delta = TOL )
self . assertAlmostEqual ( path7 . point ( 1 ) , ( 900 + 500 j ) , delta = TOL )
2016-07-06 04:51:11 +00:00
# M600,800 C625,700 725,700 750,800
path8 = CubicBezier ( 600 + 800 j , 625 + 700 j , 725 + 700 j , 750 + 800 j )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( path8 . point ( 0 ) , ( 600 + 800 j ) , delta = TOL )
self . assertAlmostEqual ( path8 . point ( 0.3 ) , ( 638.7 + 737 j ) , delta = TOL )
self . assertAlmostEqual ( path8 . point ( 0.5 ) , ( 675 + 725 j ) , delta = TOL )
self . assertAlmostEqual ( path8 . point ( 0.9 ) , ( 740.4 + 773 j ) , delta = TOL )
self . assertAlmostEqual ( path8 . point ( 1 ) , ( 750 + 800 j ) , delta = TOL )
2016-07-06 04:51:11 +00:00
# S875,900 900,800
inversion = ( 750 + 800 j ) + ( 750 + 800 j ) - ( 725 + 700 j )
path9 = CubicBezier ( 750 + 800 j , inversion , 875 + 900 j , 900 + 800 j )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( path9 . point ( 0 ) , ( 750 + 800 j ) , delta = TOL )
self . assertAlmostEqual ( path9 . point ( 0.3 ) , ( 788.7 + 863 j ) , delta = TOL )
self . assertAlmostEqual ( path9 . point ( 0.5 ) , ( 825 + 875 j ) , delta = TOL )
self . assertAlmostEqual ( path9 . point ( 0.9 ) , ( 890.4 + 827 j ) , delta = TOL )
self . assertAlmostEqual ( path9 . point ( 1 ) , ( 900 + 800 j ) , delta = TOL )
2016-07-06 04:51:11 +00:00
def test_length ( self ) :
# A straight line:
cub = CubicBezier (
complex ( 0 , 0 ) ,
complex ( 0 , 0 ) ,
complex ( 0 , 100 ) ,
complex ( 0 , 100 )
)
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( cub . length ( ) , 100 , delta = TOL )
2016-07-06 04:51:11 +00:00
# A diagonal line:
cub = CubicBezier (
complex ( 0 , 0 ) ,
complex ( 0 , 0 ) ,
complex ( 100 , 100 ) ,
complex ( 100 , 100 )
)
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( cub . length ( ) , sqrt ( 2 * 100 * 100 ) , delta = TOL )
2016-07-06 04:51:11 +00:00
2018-05-23 02:34:56 +00:00
# A quarter circle large_arc with radius 100
# http://www.whizkidtech.redprince.net/bezier/circle/
2021-06-28 05:30:56 +00:00
kappa = 4 * ( sqrt ( 2 ) - 1 ) / 3
2016-07-06 04:51:11 +00:00
cub = CubicBezier (
complex ( 0 , 0 ) ,
complex ( 0 , kappa * 100 ) ,
complex ( 100 - kappa * 100 , 100 ) ,
complex ( 100 , 100 )
)
# We can't compare with pi*50 here, because this is just an
# approximation of a circle large_arc. pi*50 is 157.079632679
# So this is just yet another "warn if this changes" test.
# This value is not verified to be correct.
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( cub . length ( ) , 157.1016698 , delta = TOL )
2016-07-06 04:51:11 +00:00
# A recursive solution has also been suggested, but for CubicBezier
# curves it could get a false solution on curves where the midpoint is
# on a straight line between the start and end. For example, the
# following curve would get solved as a straight line and get the
# length 300.
# Make sure this is not the case.
cub = CubicBezier (
complex ( 600 , 500 ) ,
complex ( 600 , 350 ) ,
complex ( 900 , 650 ) ,
complex ( 900 , 500 )
)
self . assertTrue ( cub . length ( ) > 300.0 )
def test_equality ( self ) :
# This is to test the __eq__ and __ne__ methods, so we can't use
# assertEqual and assertNotEqual
segment = CubicBezier ( complex ( 600 , 500 ) , complex ( 600 , 350 ) ,
complex ( 900 , 650 ) , complex ( 900 , 500 ) )
self . assertTrue ( segment ==
2018-05-23 02:34:56 +00:00
CubicBezier ( 600 + 500 j , 600 + 350 j , 900 + 650 j , 900 + 500 j ) )
2016-07-06 04:51:11 +00:00
self . assertTrue ( segment !=
2018-05-23 02:34:56 +00:00
CubicBezier ( 600 + 501 j , 600 + 350 j , 900 + 650 j , 900 + 500 j ) )
2016-07-06 04:51:11 +00:00
self . assertTrue ( segment != Line ( 0 , 400 ) )
class QuadraticBezierTest ( unittest . TestCase ) :
def test_svg_examples ( self ) :
""" These is the path in the SVG specs """
# M200,300 Q400,50 600,300 T1000,300
path1 = QuadraticBezier ( 200 + 300 j , 400 + 50 j , 600 + 300 j )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( path1 . point ( 0 ) , ( 200 + 300 j ) , delta = TOL )
self . assertAlmostEqual ( path1 . point ( 0.3 ) , ( 320 + 195 j ) , delta = TOL )
self . assertAlmostEqual ( path1 . point ( 0.5 ) , ( 400 + 175 j ) , delta = TOL )
self . assertAlmostEqual ( path1 . point ( 0.9 ) , ( 560 + 255 j ) , delta = TOL )
self . assertAlmostEqual ( path1 . point ( 1 ) , ( 600 + 300 j ) , delta = TOL )
2016-07-06 04:51:11 +00:00
# T1000, 300
inversion = ( 600 + 300 j ) + ( 600 + 300 j ) - ( 400 + 50 j )
path2 = QuadraticBezier ( 600 + 300 j , inversion , 1000 + 300 j )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( path2 . point ( 0 ) , ( 600 + 300 j ) , delta = TOL )
self . assertAlmostEqual ( path2 . point ( 0.3 ) , ( 720 + 405 j ) , delta = TOL )
self . assertAlmostEqual ( path2 . point ( 0.5 ) , ( 800 + 425 j ) , delta = TOL )
self . assertAlmostEqual ( path2 . point ( 0.9 ) , ( 960 + 345 j ) , delta = TOL )
self . assertAlmostEqual ( path2 . point ( 1 ) , ( 1000 + 300 j ) , delta = TOL )
2016-07-06 04:51:11 +00:00
def test_length ( self ) :
# expected results calculated with
# svg.path.segment_length(q, 0, 1, q.start, q.end, 1e-14, 20, 0)
q1 = QuadraticBezier ( 200 + 300 j , 400 + 50 j , 600 + 300 j )
q2 = QuadraticBezier ( 200 + 300 j , 400 + 50 j , 500 + 200 j )
closedq = QuadraticBezier ( 6 + 2 j , 5 - 1 j , 6 + 2 j )
linq1 = QuadraticBezier ( 1 , 2 , 3 )
linq2 = QuadraticBezier ( 1 + 3 j , 2 + 5 j , - 9 - 17 j )
nodalq = QuadraticBezier ( 1 , 1 , 1 )
tests = [ ( q1 , 487.77109389525975 ) ,
( q2 , 379.90458193489155 ) ,
( closedq , 3.1622776601683795 ) ,
( linq1 , 2 ) ,
( linq2 , 22.73335777124786 ) ,
( nodalq , 0 ) ]
for q , exp_res in tests :
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( q . length ( ) , exp_res , delta = TOL )
2016-07-06 04:51:11 +00:00
# partial length tests
tests = [ ( q1 , 212.34775387566032 ) ,
( q2 , 166.22170622052397 ) ,
( closedq , 0.7905694150420949 ) ,
( linq1 , 1.0 ) ,
( nodalq , 0.0 ) ]
t0 = 0.25
t1 = 0.75
for q , exp_res in tests :
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( q . length ( t0 = t0 , t1 = t1 ) , exp_res , delta = TOL )
2016-07-06 04:51:11 +00:00
# linear partial cases
linq2 = QuadraticBezier ( 1 + 3 j , 2 + 5 j , - 9 - 17 j )
tests = [ ( 0 , 1 / 24 , 0.13975424859373725 ) ,
( 0 , 1 / 12 , 0.1863389981249823 ) ,
( 0 , 0.5 , 4.844813951249543 ) ,
( 0 , 1 , 22.73335777124786 ) ,
( 1 / 24 , 1 / 12 , 0.04658474953124506 ) ,
( 1 / 24 , 0.5 , 4.705059702655722 ) ,
( 1 / 24 , 1 , 22.59360352265412 ) ,
( 1 / 12 , 0.5 , 4.658474953124562 ) ,
( 1 / 12 , 1 , 22.54701877312288 ) ,
( 0.5 , 1 , 17.88854381999832 ) ]
for t0 , t1 , exp_s in tests :
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( linq2 . length ( t0 = t0 , t1 = t1 ) , exp_s , delta = TOL )
2016-07-06 04:51:11 +00:00
def test_equality ( self ) :
# This is to test the __eq__ and __ne__ methods, so we can't use
# assertEqual and assertNotEqual
segment = QuadraticBezier ( 200 + 300 j , 400 + 50 j , 600 + 300 j )
2021-06-28 05:30:56 +00:00
self . assertTrue ( segment ==
2018-05-23 02:34:56 +00:00
QuadraticBezier ( 200 + 300 j , 400 + 50 j , 600 + 300 j ) )
2021-06-28 05:30:56 +00:00
self . assertTrue ( segment !=
2018-05-23 02:34:56 +00:00
QuadraticBezier ( 200 + 301 j , 400 + 50 j , 600 + 300 j ) )
2016-07-06 04:51:11 +00:00
self . assertFalse ( segment == Arc ( 0 j , 100 + 50 j , 0 , 0 , 0 , 100 + 50 j ) )
self . assertTrue ( Arc ( 0 j , 100 + 50 j , 0 , 0 , 0 , 100 + 50 j ) != segment )
class ArcTest ( unittest . TestCase ) :
2017-12-26 05:57:48 +00:00
def test_trusting_acos ( self ) :
""" `u1.real` is > 1 in this arc due to numerical error. """
try :
2021-06-28 05:30:56 +00:00
a1 = Arc ( start = ( 160.197 + 102.925 j ) ,
radius = ( 0.025 + 0.025 j ) ,
rotation = 0.0 ,
large_arc = False ,
sweep = True ,
2017-12-26 05:57:48 +00:00
end = ( 160.172 + 102.95 j ) )
except ValueError :
self . fail ( " Arc() raised ValueError unexpectedly! " )
2021-09-27 01:17:58 +00:00
def test_point ( self ) :
2016-07-06 04:51:11 +00:00
arc1 = Arc ( 0 j , 100 + 50 j , 0 , 0 , 0 , 100 + 50 j )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( arc1 . center , 100 + 0 j , delta = TOL )
self . assertAlmostEqual ( arc1 . theta , 180.0 , delta = TOL )
self . assertAlmostEqual ( arc1 . delta , - 90.0 , delta = TOL )
self . assertAlmostEqual ( arc1 . point ( 0.0 ) , 0 j , delta = TOL )
self . assertAlmostEqual ( arc1 . point ( 0.1 ) , ( 1.23116594049 + 7.82172325201 j ) , delta = TOL )
self . assertAlmostEqual ( arc1 . point ( 0.2 ) , ( 4.89434837048 + 15.4508497187 j ) , delta = TOL )
self . assertAlmostEqual ( arc1 . point ( 0.3 ) , ( 10.8993475812 + 22.699524987 j ) , delta = TOL )
self . assertAlmostEqual ( arc1 . point ( 0.4 ) , ( 19.0983005625 + 29.3892626146 j ) , delta = TOL )
self . assertAlmostEqual ( arc1 . point ( 0.5 ) , ( 29.2893218813 + 35.3553390593 j ) , delta = TOL )
self . assertAlmostEqual ( arc1 . point ( 0.6 ) , ( 41.2214747708 + 40.4508497187 j ) , delta = TOL )
self . assertAlmostEqual ( arc1 . point ( 0.7 ) , ( 54.6009500260 + 44.5503262094 j ) , delta = TOL )
self . assertAlmostEqual ( arc1 . point ( 0.8 ) , ( 69.0983005625 + 47.5528258148 j ) , delta = TOL )
self . assertAlmostEqual ( arc1 . point ( 0.9 ) , ( 84.3565534960 + 49.3844170298 j ) , delta = TOL )
self . assertAlmostEqual ( arc1 . point ( 1.0 ) , ( 100 + 50 j ) , delta = TOL )
2016-07-06 04:51:11 +00:00
arc2 = Arc ( 0 j , 100 + 50 j , 0 , 1 , 0 , 100 + 50 j )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( arc2 . center , 50 j , delta = TOL )
self . assertAlmostEqual ( arc2 . theta , - 90.0 , delta = TOL )
self . assertAlmostEqual ( arc2 . delta , - 270.0 , delta = TOL )
self . assertAlmostEqual ( arc2 . point ( 0.0 ) , 0 j , delta = TOL )
self . assertAlmostEqual ( arc2 . point ( 0.1 ) , ( - 45.399049974 + 5.44967379058 j ) , delta = TOL )
self . assertAlmostEqual ( arc2 . point ( 0.2 ) , ( - 80.9016994375 + 20.6107373854 j ) , delta = TOL )
self . assertAlmostEqual ( arc2 . point ( 0.3 ) , ( - 98.7688340595 + 42.178276748 j ) , delta = TOL )
self . assertAlmostEqual ( arc2 . point ( 0.4 ) , ( - 95.1056516295 + 65.4508497187 j ) , delta = TOL )
self . assertAlmostEqual ( arc2 . point ( 0.5 ) , ( - 70.7106781187 + 85.3553390593 j ) , delta = TOL )
self . assertAlmostEqual ( arc2 . point ( 0.6 ) , ( - 30.9016994375 + 97.5528258148 j ) , delta = TOL )
self . assertAlmostEqual ( arc2 . point ( 0.7 ) , ( 15.643446504 + 99.3844170298 j ) , delta = TOL )
self . assertAlmostEqual ( arc2 . point ( 0.8 ) , ( 58.7785252292 + 90.4508497187 j ) , delta = TOL )
self . assertAlmostEqual ( arc2 . point ( 0.9 ) , ( 89.1006524188 + 72.699524987 j ) , delta = TOL )
self . assertAlmostEqual ( arc2 . point ( 1.0 ) , ( 100 + 50 j ) , delta = TOL )
2016-07-06 04:51:11 +00:00
arc3 = Arc ( 0 j , 100 + 50 j , 0 , 0 , 1 , 100 + 50 j )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( arc3 . center , 50 j , delta = TOL )
self . assertAlmostEqual ( arc3 . theta , - 90.0 , delta = TOL )
self . assertAlmostEqual ( arc3 . delta , 90.0 , delta = TOL )
self . assertAlmostEqual ( arc3 . point ( 0.0 ) , 0 j , delta = TOL )
self . assertAlmostEqual ( arc3 . point ( 0.1 ) , ( 15.643446504 + 0.615582970243 j ) , delta = TOL )
self . assertAlmostEqual ( arc3 . point ( 0.2 ) , ( 30.9016994375 + 2.44717418524 j ) , delta = TOL )
self . assertAlmostEqual ( arc3 . point ( 0.3 ) , ( 45.399049974 + 5.44967379058 j ) , delta = TOL )
self . assertAlmostEqual ( arc3 . point ( 0.4 ) , ( 58.7785252292 + 9.54915028125 j ) , delta = TOL )
self . assertAlmostEqual ( arc3 . point ( 0.5 ) , ( 70.7106781187 + 14.6446609407 j ) , delta = TOL )
self . assertAlmostEqual ( arc3 . point ( 0.6 ) , ( 80.9016994375 + 20.6107373854 j ) , delta = TOL )
self . assertAlmostEqual ( arc3 . point ( 0.7 ) , ( 89.1006524188 + 27.300475013 j ) , delta = TOL )
self . assertAlmostEqual ( arc3 . point ( 0.8 ) , ( 95.1056516295 + 34.5491502813 j ) , delta = TOL )
self . assertAlmostEqual ( arc3 . point ( 0.9 ) , ( 98.7688340595 + 42.178276748 j ) , delta = TOL )
self . assertAlmostEqual ( arc3 . point ( 1.0 ) , ( 100 + 50 j ) , delta = TOL )
2016-07-06 04:51:11 +00:00
arc4 = Arc ( 0 j , 100 + 50 j , 0 , 1 , 1 , 100 + 50 j )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( arc4 . center , 100 + 0 j , delta = TOL )
self . assertAlmostEqual ( arc4 . theta , 180.0 , delta = TOL )
self . assertAlmostEqual ( arc4 . delta , 270.0 , delta = TOL )
self . assertAlmostEqual ( arc4 . point ( 0.0 ) , 0 j , delta = TOL )
self . assertAlmostEqual ( arc4 . point ( 0.1 ) , ( 10.8993475812 - 22.699524987 j ) , delta = TOL )
self . assertAlmostEqual ( arc4 . point ( 0.2 ) , ( 41.2214747708 - 40.4508497187 j ) , delta = TOL )
self . assertAlmostEqual ( arc4 . point ( 0.3 ) , ( 84.3565534960 - 49.3844170298 j ) , delta = TOL )
self . assertAlmostEqual ( arc4 . point ( 0.4 ) , ( 130.901699437 - 47.5528258148 j ) , delta = TOL )
self . assertAlmostEqual ( arc4 . point ( 0.5 ) , ( 170.710678119 - 35.3553390593 j ) , delta = TOL )
self . assertAlmostEqual ( arc4 . point ( 0.6 ) , ( 195.105651630 - 15.4508497187 j ) , delta = TOL )
self . assertAlmostEqual ( arc4 . point ( 0.7 ) , ( 198.768834060 + 7.82172325201 j ) , delta = TOL )
self . assertAlmostEqual ( arc4 . point ( 0.8 ) , ( 180.901699437 + 29.3892626146 j ) , delta = TOL )
self . assertAlmostEqual ( arc4 . point ( 0.9 ) , ( 145.399049974 + 44.5503262094 j ) , delta = TOL )
self . assertAlmostEqual ( arc4 . point ( 1.0 ) , ( 100 + 50 j ) , delta = TOL )
2016-07-06 04:51:11 +00:00
arc5 = Arc ( ( 725.307482225571 - 915.5548199281527 j ) ,
( 202.79421639137703 + 148.77294617167183 j ) ,
225.6910319606926 , 1 , 1 ,
( - 624.6375539637027 + 896.5483089399895 j ) )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( arc5 . point ( 0.0 ) , ( 725.307482226 - 915.554819928 j ) , delta = TOL )
self . assertAlmostEqual ( arc5 . point ( 0.0909090909091 ) ,
2018-05-23 02:34:56 +00:00
( 1023.47397369 - 597.730444283 j ) )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( arc5 . point ( 0.181818181818 ) ,
2018-05-23 02:34:56 +00:00
( 1242.80253007 - 232.251400124 j ) )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( arc5 . point ( 0.272727272727 ) ,
2018-05-23 02:34:56 +00:00
( 1365.52445614 + 151.273373978 j ) )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( arc5 . point ( 0.363636363636 ) ,
2018-05-23 02:34:56 +00:00
( 1381.69755131 + 521.772981736 j ) )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( arc5 . point ( 0.454545454545 ) ,
2018-05-23 02:34:56 +00:00
( 1290.01156757 + 849.231748376 j ) )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( arc5 . point ( 0.545454545455 ) ,
2018-05-23 02:34:56 +00:00
( 1097.89435807 + 1107.12091209 j ) )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( arc5 . point ( 0.636363636364 ) ,
2018-05-23 02:34:56 +00:00
( 820.910116547 + 1274.54782658 j ) )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( arc5 . point ( 0.727272727273 ) ,
2018-05-23 02:34:56 +00:00
( 481.49845896 + 1337.94855893 j ) )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( arc5 . point ( 0.818181818182 ) ,
2018-05-23 02:34:56 +00:00
( 107.156499251 + 1292.18675889 j ) )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( arc5 . point ( 0.909090909091 ) ,
2018-05-23 02:34:56 +00:00
( - 271.788803303 + 1140.96977533 j ) )
2016-07-06 04:51:11 +00:00
def test_length ( self ) :
# I'll test the length calculations by making a circle, in two parts.
arc1 = Arc ( 0 j , 100 + 100 j , 0 , 0 , 0 , 200 + 0 j )
arc2 = Arc ( 200 + 0 j , 100 + 100 j , 0 , 0 , 0 , 0 j )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( arc1 . length ( ) , pi * 100 , delta = TOL )
self . assertAlmostEqual ( arc2 . length ( ) , pi * 100 , delta = TOL )
2016-07-06 04:51:11 +00:00
def test_equality ( self ) :
# This is to test the __eq__ and __ne__ methods, so we can't use
# assertEqual and assertNotEqual
segment = Arc ( 0 j , 100 + 50 j , 0 , 0 , 0 , 100 + 50 j )
self . assertTrue ( segment == Arc ( 0 j , 100 + 50 j , 0 , 0 , 0 , 100 + 50 j ) )
self . assertTrue ( segment != Arc ( 0 j , 100 + 50 j , 0 , 1 , 0 , 100 + 50 j ) )
2018-11-05 04:55:17 +00:00
def test_point_to_t ( self ) :
2021-06-28 05:30:56 +00:00
tol = 1e-4
2018-11-05 04:55:17 +00:00
a = Arc ( start = ( 0 + 0 j ) , radius = ( 5 + 5 j ) , rotation = 0.0 , large_arc = True , sweep = True , end = ( 0 + 10 j ) )
self . assertEqual ( a . point_to_t ( 0 + 0 j ) , 0.0 )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( a . point_to_t ( 5 + 5 j ) , 0.5 , delta = tol )
2018-11-05 04:55:17 +00:00
self . assertEqual ( a . point_to_t ( 0 + 10 j ) , 1.0 )
self . assertIsNone ( a . point_to_t ( - 5 + 5 j ) )
self . assertIsNone ( a . point_to_t ( 0 + 5 j ) )
self . assertIsNone ( a . point_to_t ( 1 + 0 j ) )
self . assertIsNone ( a . point_to_t ( 0 - 1 j ) )
self . assertIsNone ( a . point_to_t ( 0 + 11 j ) )
a = Arc ( start = ( 0 + 0 j ) , radius = ( 5 + 5 j ) , rotation = 0.0 , large_arc = True , sweep = False , end = ( 0 + 10 j ) )
self . assertEqual ( a . point_to_t ( 0 + 0 j ) , 0.0 )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( a . point_to_t ( - 5 + 5 j ) , 0.5 , delta = tol )
2018-11-05 04:55:17 +00:00
self . assertEqual ( a . point_to_t ( 0 + 10 j ) , 1.0 )
self . assertIsNone ( a . point_to_t ( 5 + 5 j ) )
self . assertIsNone ( a . point_to_t ( 0 + 5 j ) )
self . assertIsNone ( a . point_to_t ( 1 + 0 j ) )
self . assertIsNone ( a . point_to_t ( 0 - 1 j ) )
self . assertIsNone ( a . point_to_t ( 0 + 11 j ) )
a = Arc ( start = ( - 10 + 0 j ) , radius = ( 10 + 20 j ) , rotation = 0.0 , large_arc = True , sweep = True , end = ( 10 + 0 j ) )
self . assertEqual ( a . point_to_t ( - 10 + 0 j ) , 0.0 )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( a . point_to_t ( 0 - 20 j ) , 0.5 , delta = tol )
2018-11-05 04:55:17 +00:00
self . assertEqual ( a . point_to_t ( 10 + 0 j ) , 1.0 )
self . assertIsNone ( a . point_to_t ( 0 + 20 j ) )
self . assertIsNone ( a . point_to_t ( - 5 + 5 j ) )
self . assertIsNone ( a . point_to_t ( 0 + 5 j ) )
self . assertIsNone ( a . point_to_t ( 1 + 0 j ) )
self . assertIsNone ( a . point_to_t ( 0 - 1 j ) )
self . assertIsNone ( a . point_to_t ( 0 + 11 j ) )
a = Arc ( start = ( 100.834 + 27.987 j ) , radius = ( 60.6 + 60.6 j ) , rotation = 0.0 , large_arc = False , sweep = False , end = ( 40.234 - 32.613 j ) )
self . assertEqual ( a . point_to_t ( 100.834 + 27.987 j ) , 0.0 )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( a . point_to_t ( 96.2210993246 + 4.7963831644 j ) , 0.25 , delta = tol )
self . assertAlmostEqual ( a . point_to_t ( 83.0846703014 - 14.8636715784 j ) , 0.50 , delta = tol )
self . assertAlmostEqual ( a . point_to_t ( 63.4246151671 - 28.0001000158 j ) , 0.75 , delta = tol )
2018-11-05 04:55:17 +00:00
self . assertEqual ( a . point_to_t ( 40.234 - 32.613 j ) , 1.00 )
self . assertIsNone ( a . point_to_t ( - 10 + 0 j ) )
self . assertIsNone ( a . point_to_t ( 0 + 0 j ) )
a = Arc ( start = ( 423.049961698 - 41.3779390229 j ) , radius = ( 904.283878032 + 597.298520765 j ) , rotation = 0.0 , large_arc = True , sweep = False , end = ( 548.984030235 - 312.385118044 j ) )
orig_t = 0.854049465076
p = a . point ( orig_t )
computed_t = a . point_to_t ( p )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( orig_t , computed_t , delta = TOL )
2018-11-05 04:55:17 +00:00
a = Arc ( start = ( - 1 - 750 j ) , radius = ( 750 + 750 j ) , rotation = 0.0 , large_arc = True , sweep = False , end = 1 - 750 j )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( a . point_to_t ( 730.5212132777968 + 169.8191111892562 j ) , 0.71373858 , delta = tol )
2018-11-05 04:55:17 +00:00
self . assertIsNone ( a . point_to_t ( 730.5212132777968 + 169 j ) )
self . assertIsNone ( a . point_to_t ( 730.5212132777968 + 171 j ) )
random . seed ( )
for arc_index in range ( 100 ) :
a = random_arc ( )
2021-06-28 05:30:56 +00:00
for t_index in np . linspace ( 0 , 1 , 100 ) :
2018-11-05 04:55:17 +00:00
orig_t = random . random ( )
p = a . point ( orig_t )
computed_t = a . point_to_t ( p )
2021-06-28 05:30:56 +00:00
msg = " arc %s at t= %f is point %s , but got %f back " \
" " % ( a , orig_t , p , computed_t )
self . assertAlmostEqual ( orig_t , computed_t , msg = msg , delta = tol )
2018-11-05 04:55:17 +00:00
2020-12-02 02:37:15 +00:00
def test_approx_quad ( self ) :
n = 100
for i in range ( n ) :
arc = random_arc ( )
if arc . radius . real > 2000 or arc . radius . imag > 2000 :
continue # Random Arc too large, by autoscale.
path1 = Path ( arc )
path2 = Path ( * path1 )
path2 . approximate_arcs_with_quads ( error = 0.05 )
d = abs ( path1 . length ( ) - path2 . length ( ) )
# Error less than 1% typically less than 0.5%
self . assertAlmostEqual ( d , 0.0 , delta = 20 )
def test_approx_cubic ( self ) :
n = 100
for i in range ( n ) :
arc = random_arc ( )
if arc . radius . real > 2000 or arc . radius . imag > 2000 :
continue # Random Arc too large, by autoscale.
path1 = Path ( arc )
path2 = Path ( * path1 )
path2 . approximate_arcs_with_cubics ( error = 0.1 )
d = abs ( path1 . length ( ) - path2 . length ( ) )
# Error less than 0.1% typically less than 0.001%
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( d , 0.0 , delta = 2 )
2020-12-02 02:37:15 +00:00
2016-07-06 04:51:11 +00:00
class TestPath ( unittest . TestCase ) :
2021-11-02 01:05:23 +00:00
def test_hash ( self ) :
line1 = Line ( 600.5 + 350.5 j , 650.5 + 325.5 j )
arc1 = Arc ( 650 + 325 j , 25 + 25 j , - 30 , 0 , 1 , 700 + 300 j )
arc2 = Arc ( 650 + 325 j , 30 + 25 j , - 30 , 0 , 0 , 700 + 300 j )
cub1 = CubicBezier ( 650 + 325 j , 25 + 25 j , - 30 , 700 + 300 j )
cub2 = CubicBezier ( 700 + 300 j , 800 + 400 j , 750 + 200 j , 600 + 100 j )
quad3 = QuadraticBezier ( 600 + 100 j , 600 , 600 + 300 j )
linez = Line ( 600 + 300 j , 600 + 350 j )
bezpath = Path ( line1 , cub1 , cub2 , quad3 )
bezpathz = Path ( line1 , cub1 , cub2 , quad3 , linez )
path = Path ( line1 , arc1 , cub2 , quad3 )
pathz = Path ( line1 , arc1 , cub2 , quad3 , linez )
lpath = Path ( linez )
qpath = Path ( quad3 )
cpath = Path ( cub1 )
apath = Path ( arc1 , arc2 )
test_curves = [ bezpath , bezpathz , path , pathz , lpath , qpath , cpath ,
apath , line1 , arc1 , arc2 , cub1 , cub2 , quad3 , linez ]
# this is necessary due to changes to the builtin `hash` function
user_hash_seed = os . environ . get ( " PYTHONHASHSEED " , " " )
os . environ [ " PYTHONHASHSEED " ] = " 314 "
2021-11-10 03:48:41 +00:00
if version_info > = ( 3 , 8 ) :
2021-11-02 01:05:23 +00:00
expected_hashes = [
- 6073024107272494569 , - 2519772625496438197 , 8726412907710383506 ,
2132930052750006195 , 3112548573593977871 , 991446120749438306 ,
- 5589397644574569777 , - 4438808571483114580 , - 3125333407400456536 ,
- 4418099728831808951 , 702646573139378041 , - 6331016786776229094 ,
5053050772929443013 , 6102272282813527681 , - 5385294438006156225
]
2021-11-10 03:48:41 +00:00
elif ( 3 , 2 ) < = version_info < ( 3 , 8 ) :
2021-11-02 01:05:23 +00:00
expected_hashes = [
- 5662973462929734898 , 5166874115671195563 , 5223434942701471389 ,
- 7224979960884350294 , - 5178990533869800243 , - 4003140762934044601 ,
8575549467429100514 , - 6692132994808317852 , 1594848578230132678 ,
- 6374833902132909499 , 4188352014604112779 , - 5090374009174854814 ,
- 7093907105533857815 , 2036243740727202243 , - 8108488067585685407
]
else :
2021-11-10 03:48:41 +00:00
2021-11-02 01:05:23 +00:00
expected_hashes = [
- 5762846476463470127 , - 138736730317965290 , - 2005041722222729058 ,
8448700906794235291 , - 5178990533869800243 , - 4003140762934044601 ,
8575549467429100514 , 5166859065265868968 , 1373103287265872323 ,
- 1022491904150314631 , 4188352014604112779 , - 5090374009174854814 ,
- 7093907105533857815 , 2036243740727202243 , - 8108488067585685407
]
2021-11-10 03:48:41 +00:00
if version_info . major == 2 and os . name == ' nt ' :
# the expected hash values for 2.7 apparently differed on Windows
# if you work in Windows and want to fix this test, please do
return
2021-11-02 01:05:23 +00:00
for c , h in zip ( test_curves , expected_hashes ) :
self . assertTrue ( hash ( c ) == h , msg = " hash {} was expected for curve = {} " . format ( h , c ) )
os . environ [ " PYTHONHASHSEED " ] = user_hash_seed # restore user's hash seed
2021-09-23 04:19:13 +00:00
2016-07-06 04:51:11 +00:00
def test_circle ( self ) :
arc1 = Arc ( 0 j , 100 + 100 j , 0 , 0 , 0 , 200 + 0 j )
arc2 = Arc ( 200 + 0 j , 100 + 100 j , 0 , 0 , 0 , 0 j )
path = Path ( arc1 , arc2 )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( path . point ( 0.0 ) , 0 j , delta = TOL )
self . assertAlmostEqual ( path . point ( 0.25 ) , ( 100 + 100 j ) , delta = TOL )
self . assertAlmostEqual ( path . point ( 0.5 ) , ( 200 + 0 j ) , delta = TOL )
self . assertAlmostEqual ( path . point ( 0.75 ) , ( 100 - 100 j ) , delta = TOL )
self . assertAlmostEqual ( path . point ( 1.0 ) , 0 j , delta = TOL )
self . assertAlmostEqual ( path . length ( ) , pi * 200 , delta = TOL )
2016-07-06 04:51:11 +00:00
def test_svg_specs ( self ) :
""" The paths that are in the SVG specs """
# Big pie: M300,200 h-150 a150,150 0 1,0 150,-150 z
path = Path ( Line ( 300 + 200 j , 150 + 200 j ) ,
Arc ( 150 + 200 j , 150 + 150 j , 0 , 1 , 0 , 300 + 50 j ) ,
Line ( 300 + 50 j , 300 + 200 j ) )
2021-06-28 05:30:56 +00:00
# The points and length for this path are calculated and not
2018-05-23 02:34:56 +00:00
# regression tests.
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( path . point ( 0.0 ) , ( 300 + 200 j ) , delta = TOL )
self . assertAlmostEqual ( path . point ( 0.14897825542 ) , ( 150 + 200 j ) , delta = TOL )
self . assertAlmostEqual ( path . point ( 0.5 ) , ( 406.066017177 + 306.066017177 j ) , delta = TOL )
self . assertAlmostEqual ( path . point ( 1 - 0.14897825542 ) , ( 300 + 50 j ) , delta = TOL )
self . assertAlmostEqual ( path . point ( 1.0 ) , ( 300 + 200 j ) , delta = TOL )
# The errors seem to accumulate. Still 6 decimal places is more
2018-05-23 02:34:56 +00:00
# than good enough.
2016-07-06 04:51:11 +00:00
self . assertAlmostEqual ( path . length ( ) , pi * 225 + 300 , places = 6 )
# Little pie: M275,175 v-150 a150,150 0 0,0 -150,150 z
path = Path ( Line ( 275 + 175 j , 275 + 25 j ) ,
Arc ( 275 + 25 j , 150 + 150 j , 0 , 0 , 0 , 125 + 175 j ) ,
Line ( 125 + 175 j , 275 + 175 j ) )
2021-06-28 05:30:56 +00:00
# The points and length for this path are calculated and not
2018-05-23 02:34:56 +00:00
# regression tests.
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( path . point ( 0.0 ) , ( 275 + 175 j ) , delta = TOL )
self . assertAlmostEqual ( path . point ( 0.2800495767557787 ) , ( 275 + 25 j ) , delta = TOL )
self . assertAlmostEqual ( path . point ( 0.5 ) ,
2018-05-23 02:34:56 +00:00
( 168.93398282201787 + 68.93398282201787 j ) )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( path . point ( 1 - 0.2800495767557787 ) , ( 125 + 175 j ) , delta = TOL )
self . assertAlmostEqual ( path . point ( 1.0 ) , ( 275 + 175 j ) , delta = TOL )
# The errors seem to accumulate. Still 6 decimal places is more
2018-05-23 02:34:56 +00:00
# than good enough.
2016-07-06 04:51:11 +00:00
self . assertAlmostEqual ( path . length ( ) , pi * 75 + 300 , places = 6 )
# Bumpy path: M600,350 l 50,-25
# a25,25 -30 0,1 50,-25 l 50,-25
# a25,50 -30 0,1 50,-25 l 50,-25
# a25,75 -30 0,1 50,-25 l 50,-25
# a25,100 -30 0,1 50,-25 l 50,-25
# Commented out because by Andy cause I was skeptical of path.point
# ground truth values
# path = Path(Line(600 + 350j, 650 + 325j),
# Arc(650 + 325j, 25 + 25j, -30, 0, 1, 700 + 300j),
# Line(700 + 300j, 750 + 275j),
# Arc(750 + 275j, 25 + 50j, -30, 0, 1, 800 + 250j),
# Line(800 + 250j, 850 + 225j),
# Arc(850 + 225j, 25 + 75j, -30, 0, 1, 900 + 200j),
# Line(900 + 200j, 950 + 175j),
# Arc(950 + 175j, 25 + 100j, -30, 0, 1, 1000 + 150j),
# Line(1000 + 150j, 1050 + 125j),
# )
# # These are *not* calculated, but just regression tests. Be skeptical.
2021-06-28 05:30:56 +00:00
# self.assertAlmostEqual(path.point(0), (600+350j), delta=TOL)
# self.assertAlmostEqual(path.point(0.3), (755.239799276+212.182020958j), delta=TOL)
# self.assertAlmostEqual(path.point(0.5), (827.730749264+147.824157418j), delta=TOL)
# self.assertAlmostEqual(path.point(0.9), (971.284357806+106.302352605j), delta=TOL)
# self.assertAlmostEqual(path.point(1), (1050+125j), delta=TOL)
# # The errors seem to accumulate. Still 6 decimal places is more
2018-05-23 02:34:56 +00:00
# # than good enough.
2021-06-28 05:30:56 +00:00
# self.assertAlmostEqual(path.length(), 928.3886394081095, delta=TOL)
2016-07-06 04:51:11 +00:00
def test_repr ( self ) :
path = Path (
Line ( start = 600 + 350 j , end = 650 + 325 j ) ,
2021-06-28 05:30:56 +00:00
Arc ( start = 650 + 325 j , radius = 25 + 25 j , rotation = - 30 ,
2018-05-23 02:34:56 +00:00
large_arc = 0 , sweep = 1 , end = 700 + 300 j ) ,
2021-06-28 05:30:56 +00:00
CubicBezier ( start = 700 + 300 j , control1 = 800 + 400 j ,
2018-05-23 02:34:56 +00:00
control2 = 750 + 200 j , end = 600 + 100 j ) ,
2016-07-06 04:51:11 +00:00
QuadraticBezier ( start = 600 + 100 j , control = 600 , end = 600 + 300 j ) )
self . assertEqual ( eval ( repr ( path ) ) , path )
def test_equality ( self ) :
# This is to test the __eq__ and __ne__ methods, so we can't use
# assertEqual and assertNotEqual
path1 = Path (
Line ( start = 600 + 350 j , end = 650 + 325 j ) ,
2021-06-28 05:30:56 +00:00
Arc ( start = 650 + 325 j , radius = 25 + 25 j , rotation = - 30 ,
2018-05-23 02:34:56 +00:00
large_arc = 0 , sweep = 1 , end = 700 + 300 j ) ,
2021-06-28 05:30:56 +00:00
CubicBezier ( start = 700 + 300 j , control1 = 800 + 400 j ,
2018-05-23 02:34:56 +00:00
control2 = 750 + 200 j , end = 600 + 100 j ) ,
2016-07-06 04:51:11 +00:00
QuadraticBezier ( start = 600 + 100 j , control = 600 , end = 600 + 300 j ) )
path2 = Path (
Line ( start = 600 + 350 j , end = 650 + 325 j ) ,
2021-06-28 05:30:56 +00:00
Arc ( start = 650 + 325 j , radius = 25 + 25 j , rotation = - 30 ,
2018-05-23 02:34:56 +00:00
large_arc = 0 , sweep = 1 , end = 700 + 300 j ) ,
2021-06-28 05:30:56 +00:00
CubicBezier ( start = 700 + 300 j , control1 = 800 + 400 j ,
2018-05-23 02:34:56 +00:00
control2 = 750 + 200 j , end = 600 + 100 j ) ,
2016-07-06 04:51:11 +00:00
QuadraticBezier ( start = 600 + 100 j , control = 600 , end = 600 + 300 j ) )
self . assertTrue ( path1 == path2 )
# Modify path2:
path2 [ 0 ] . start = 601 + 350 j
self . assertTrue ( path1 != path2 )
# Modify back:
path2 [ 0 ] . start = 600 + 350 j
self . assertFalse ( path1 != path2 )
# Get rid of the last segment:
del path2 [ - 1 ]
self . assertFalse ( path1 == path2 )
# It's not equal to a list of it's segments
self . assertTrue ( path1 != path1 [ : ] )
self . assertFalse ( path1 == path1 [ : ] )
def test_continuous_subpaths ( self ) :
""" Test the Path.continuous_subpaths() method. """
# Continuous and open example
q = Path ( Line ( 1 , 2 ) )
a = [ Path ( Line ( 1 , 2 ) ) ]
subpaths = q . continuous_subpaths ( )
chk1 = all ( subpath . iscontinuous ( ) for subpath in subpaths )
chk2 = ( q == Path ( * [ seg for subpath in subpaths for seg in subpath ] ) )
self . assertTrue ( subpaths == a )
self . assertTrue ( chk1 )
self . assertTrue ( chk2 )
# # Continuous and closed example
q = Path ( Line ( 1 , 2 ) , Line ( 2 , 1 ) )
a = [ Path ( Line ( 1 , 2 ) , Line ( 2 , 1 ) ) ]
subpaths = q . continuous_subpaths ( )
chk1 = all ( subpath . iscontinuous ( ) for subpath in subpaths )
chk2 = q == Path ( * [ seg for subpath in subpaths for seg in subpath ] )
self . assertTrue ( subpaths == a )
self . assertTrue ( chk1 )
self . assertTrue ( chk2 )
# Continuous and open example
q = Path ( Line ( 1 , 2 ) , Line ( 2 , 3 ) , Line ( 3 , 4 ) )
a = [ Path ( Line ( 1 , 2 ) , Line ( 2 , 3 ) , Line ( 3 , 4 ) ) ]
subpaths = q . continuous_subpaths ( )
chk1 = all ( subpath . iscontinuous ( ) for subpath in subpaths )
chk2 = ( q == Path ( * [ seg for subpath in subpaths for seg in subpath ] ) )
self . assertTrue ( subpaths == a )
self . assertTrue ( chk1 )
self . assertTrue ( chk2 )
# Continuous and closed example
q = Path ( Line ( 1 , 2 ) , Line ( 2 , 3 ) , Line ( 3 , 4 ) , Line ( 4 , 1 ) )
a = [ Path ( Line ( 1 , 2 ) , Line ( 2 , 3 ) , Line ( 3 , 4 ) , Line ( 4 , 1 ) ) ]
subpaths = q . continuous_subpaths ( )
chk1 = all ( subpath . iscontinuous ( ) for subpath in subpaths )
chk2 = ( q == Path ( * [ seg for subpath in subpaths for seg in subpath ] ) )
self . assertTrue ( subpaths == a )
self . assertTrue ( chk1 )
self . assertTrue ( chk2 )
# Discontinuous example
q = Path ( Line ( 1 , 2 ) , Line ( 2 , 3 ) , Line ( 3 , 4 ) ,
Line ( 10 , 11 ) )
a = [ Path ( Line ( 1 , 2 ) , Line ( 2 , 3 ) , Line ( 3 , 4 ) ) ,
Path ( Line ( 10 , 11 ) ) ]
subpaths = q . continuous_subpaths ( )
chk1 = all ( subpath . iscontinuous ( ) for subpath in subpaths )
chk2 = ( q == Path ( * [ seg for subpath in subpaths for seg in subpath ] ) )
self . assertTrue ( subpaths == a )
self . assertTrue ( chk1 )
self . assertTrue ( chk2 )
# Discontinuous closed example
q = Path ( Line ( 1 , 2 ) , Line ( 2 , 3 ) , Line ( 3 , 4 ) , Line ( 4 , 1 ) ,
Line ( 10 , 11 ) , Line ( 11 , 12 ) )
a = [ Path ( Line ( 1 , 2 ) , Line ( 2 , 3 ) , Line ( 3 , 4 ) , Line ( 4 , 1 ) ) ,
Path ( Line ( 10 , 11 ) , Line ( 11 , 12 ) ) ]
subpaths = q . continuous_subpaths ( )
chk1 = all ( subpath . iscontinuous ( ) for subpath in subpaths )
chk2 = ( q == Path ( * [ seg for subpath in subpaths for seg in subpath ] ) )
self . assertTrue ( subpaths == a )
self . assertTrue ( chk1 )
self . assertTrue ( chk2 )
# Discontinuous example
q = Path ( Line ( 1 , 2 ) ,
Line ( 1 , 2 ) , Line ( 2 , 3 ) ,
Line ( 10 , 11 ) , Line ( 11 , 12 ) , Line ( 12 , 13 ) ,
Line ( 10 , 11 ) , Line ( 11 , 12 ) , Line ( 12 , 13 ) , Line ( 13 , 14 ) )
a = [ Path ( Line ( 1 , 2 ) ) ,
Path ( Line ( 1 , 2 ) , Line ( 2 , 3 ) ) ,
Path ( Line ( 10 , 11 ) , Line ( 11 , 12 ) , Line ( 12 , 13 ) ) ,
Path ( Line ( 10 , 11 ) , Line ( 11 , 12 ) , Line ( 12 , 13 ) , Line ( 13 , 14 ) ) ]
subpaths = q . continuous_subpaths ( )
chk1 = all ( subpath . iscontinuous ( ) for subpath in subpaths )
chk2 = ( q == Path ( * [ seg for subpath in subpaths for seg in subpath ] ) )
self . assertTrue ( subpaths == a )
self . assertTrue ( chk1 )
self . assertTrue ( chk2 )
# Discontinuous example with overlapping end
q = Path ( Line ( 1 , 2 ) ,
Line ( 5 , 6 ) , Line ( 6 , 7 ) ,
Line ( 10 , 11 ) , Line ( 11 , 12 ) , Line ( 12 , 13 ) ,
Line ( 10 , 11 ) , Line ( 11 , 12 ) , Line ( 12 , 13 ) , Line ( 13 , 1 ) )
a = [ Path ( Line ( 1 , 2 ) ) ,
Path ( Line ( 5 , 6 ) , Line ( 6 , 7 ) ) ,
Path ( Line ( 10 , 11 ) , Line ( 11 , 12 ) , Line ( 12 , 13 ) ) ,
Path ( Line ( 10 , 11 ) , Line ( 11 , 12 ) , Line ( 12 , 13 ) , Line ( 13 , 1 ) ) ]
subpaths = q . continuous_subpaths ( )
chk1 = all ( subpath . iscontinuous ( ) for subpath in subpaths )
chk2 = ( q == Path ( * [ seg for subpath in subpaths for seg in subpath ] ) )
self . assertTrue ( subpaths == a )
self . assertTrue ( chk1 )
self . assertTrue ( chk2 )
2017-03-30 08:38:27 +00:00
def test_cropped ( self ) :
p_closed = Path ( Line ( 0 , 1 ) , Line ( 1 , 1 + 1 j ) , Line ( 1 + 1 j , 1 j ) ,
Line ( 1 j , 0 ) )
first_half = Path ( Line ( 0 , 1 ) , Line ( 1 , 1 + 1 j ) )
second_half = Path ( Line ( 1 + 1 j , 1 j ) , Line ( 1 j , 0 ) )
middle_half = Path ( Line ( 1 , 1 + 1 j ) , Line ( 1 + 1 j , 1 j ) )
other_middle_half = Path ( Line ( 1 j , 0 ) , Line ( 0 , 1 ) )
self . assertTrue ( p_closed . cropped ( 0 , 0.5 ) == first_half )
self . assertTrue ( p_closed . cropped ( 1 , 0.5 ) == first_half )
self . assertTrue ( p_closed . cropped ( .5 , 1 ) == second_half )
self . assertTrue ( p_closed . cropped ( 0.25 , 0.75 ) == middle_half )
self . assertTrue ( p_closed . cropped ( 0.75 , 0.25 ) == other_middle_half )
with self . assertRaises ( AssertionError ) :
p_closed . cropped ( 1 , 0 )
with self . assertRaises ( AssertionError ) :
p_closed . cropped ( .5 , 1.1 )
with self . assertRaises ( AssertionError ) :
p_closed . cropped ( - 0.1 , 0.1 )
p_open = Path ( Line ( 0 , 1 ) , Line ( 1 , 1 + 1 j ) , Line ( 1 + 1 j , 1 j ) ,
Line ( 1 j , 2 j ) )
self . assertTrue ( p_open . cropped ( 0 , 0.5 ) == first_half )
with self . assertRaises ( ValueError ) :
p_open . cropped ( .75 , .25 )
with self . assertRaises ( ValueError ) :
p_open . cropped ( 1 , .25 )
with self . assertRaises ( AssertionError ) :
p_open . cropped ( 1 , 0 )
2018-05-31 02:30:24 +00:00
def test_transform_scale ( self ) :
2018-06-09 05:13:18 +00:00
2018-05-31 02:30:24 +00:00
line1 = Line ( 600.5 + 350.5 j , 650.5 + 325.5 j )
2018-05-22 22:48:45 +00:00
arc1 = Arc ( 650 + 325 j , 25 + 25 j , - 30 , 0 , 1 , 700 + 300 j )
2018-05-31 02:30:24 +00:00
arc2 = Arc ( 650 + 325 j , 30 + 25 j , - 30 , 0 , 0 , 700 + 300 j )
2018-05-22 22:48:45 +00:00
cub1 = CubicBezier ( 650 + 325 j , 25 + 25 j , - 30 , 700 + 300 j )
cub2 = CubicBezier ( 700 + 300 j , 800 + 400 j , 750 + 200 j , 600 + 100 j )
quad3 = QuadraticBezier ( 600 + 100 j , 600 , 600 + 300 j )
linez = Line ( 600 + 300 j , 600 + 350 j )
bezpath = Path ( line1 , cub1 , cub2 , quad3 )
bezpathz = Path ( line1 , cub1 , cub2 , quad3 , linez )
path = Path ( line1 , arc1 , cub2 , quad3 )
pathz = Path ( line1 , arc1 , cub2 , quad3 , linez )
lpath = Path ( linez )
qpath = Path ( quad3 )
cpath = Path ( cub1 )
2018-05-31 02:30:24 +00:00
apath = Path ( arc1 , arc2 )
2018-05-22 22:48:45 +00:00
2018-06-09 05:13:18 +00:00
test_curves = [ bezpath , bezpathz , path , pathz , lpath , qpath , cpath ,
apath , line1 , arc1 , arc2 , cub1 , cub2 , quad3 , linez ]
def scale_a_point ( pt , sx , sy = None , origin = 0 j ) :
if sy is None :
sy = sx
zeta = pt - origin
pt_vec = [ [ zeta . real ] ,
[ zeta . imag ] ,
[ 1 ] ]
transform = [ [ sx , 0 , origin . real ] ,
[ 0 , sy , origin . imag ] ]
return complex ( * np . dot ( transform , pt_vec ) . ravel ( ) )
for curve in test_curves :
# generate a random point and a random scaling
t = np . random . rand ( )
pt = curve . point ( t )
# random diagonal transformation
sx = 2 * np . random . rand ( )
sy = 2 * np . random . rand ( )
# random origin
origin = ( 10 * ( np . random . rand ( ) - 0.5 ) +
10 j * ( np . random . rand ( ) - 0.5 ) )
2018-05-22 22:48:45 +00:00
2018-06-09 05:13:18 +00:00
# Note: `sx != sy` cases are not implemented for `Arc` objects
has_arc = ( isinstance ( curve , Arc ) or
isinstance ( curve , Path ) and
any ( isinstance ( seg , Arc ) for seg in curve ) )
2018-05-22 22:48:45 +00:00
2018-07-07 02:59:28 +00:00
# find seg which t lands on for failure reporting
seg = curve
if isinstance ( curve , Path ) :
seg_idx , seg_t = curve . T2t ( t )
seg = curve [ seg_idx ]
_fail_msg = " Failure! \n seg {} \n " . format ( seg )
2018-06-09 05:13:18 +00:00
# case where no `sy` and no `origin` given
2018-07-07 02:59:28 +00:00
curve_scaled = curve . scaled ( sx )
if isinstance ( curve , Path ) :
res = curve_scaled [ seg_idx ] . point ( seg_t )
else :
res = curve_scaled . point ( t )
2018-06-09 05:13:18 +00:00
ans = scale_a_point ( pt , sx , None )
2018-07-07 02:59:28 +00:00
fail_msg = _fail_msg + ( " curve.scaled( {} , {} , {} ) = \n {} \n "
" " . format ( sx , None , None , curve_scaled ) )
fail_msg + = " seg_scaled.point( {} ) = {} \n " . format ( seg_t , res )
fail_msg + = " ans = {} " . format ( ans )
self . assertAlmostEqual ( ans , res , places = 4 , msg = fail_msg )
# case where random `origin` given but no `sy`
2018-06-09 05:13:18 +00:00
ans = scale_a_point ( pt , sx , None , origin )
2018-07-07 02:59:28 +00:00
curve_scaled = curve . scaled ( sx , origin = origin )
if isinstance ( curve , Path ) :
res = curve_scaled [ seg_idx ] . point ( seg_t )
else :
res = curve_scaled . point ( t )
fail_msg = _fail_msg + ( " curve.scaled( {} , {} , {} ) = \n {} \n "
" " . format ( sx , None , origin , curve_scaled ) )
fail_msg + = " seg_scaled.point( {} ) = {} \n " . format ( seg_t , res )
fail_msg + = " ans = {} " . format ( ans )
self . assertAlmostEqual ( ans , res , places = 4 , msg = fail_msg )
2018-06-09 05:13:18 +00:00
# case where `sx != sy`, and no `origin` given
ans = scale_a_point ( pt , sx , sy )
2018-07-07 02:59:28 +00:00
if has_arc : # the cases with sx != sy are not yet imp for arcs
with self . assertRaises ( Exception ) :
curve . scaled ( sx , sy ) . point ( t )
2018-06-09 05:13:18 +00:00
else :
2018-07-07 02:59:28 +00:00
curve_scaled = curve . scaled ( sx , sy )
seg_scaled = seg . scaled ( sx , sy )
if isinstance ( curve , Path ) :
res = curve_scaled [ seg_idx ] . point ( seg_t )
else :
res = curve_scaled . point ( t )
fail_msg = _fail_msg + ( " curve.scaled( {} , {} , {} ) = \n {} \n "
" " . format ( sx , sy , None , curve_scaled ) )
fail_msg + = " seg_scaled.point( {} ) = {} \n " . format ( seg_t , res )
fail_msg + = " ans = {} " . format ( ans )
self . assertAlmostEqual ( ans , res , places = 4 , msg = fail_msg )
2018-06-09 05:13:18 +00:00
# case where `sx != sy`, and random `origin` given
ans = scale_a_point ( pt , sx , sy , origin )
2018-07-07 02:59:28 +00:00
if has_arc : # the cases with sx != sy are not yet imp for arcs
with self . assertRaises ( Exception ) :
curve . scaled ( sx , sy , origin ) . point ( t )
2018-06-09 05:13:18 +00:00
else :
2018-07-07 02:59:28 +00:00
curve_scaled = curve . scaled ( sx , sy , origin )
if isinstance ( curve , Path ) :
res = curve_scaled [ seg_idx ] . point ( seg_t )
else :
res = curve_scaled . point ( t )
fail_msg = _fail_msg + ( " curve.scaled( {} , {} , {} ) = \n {} \n "
" " . format ( sx , sy , origin , curve_scaled ) )
fail_msg + = " seg_scaled.point( {} ) = {} \n " . format ( seg_t , res )
fail_msg + = " ans = {} " . format ( ans )
self . assertAlmostEqual ( ans , res , places = 4 , msg = fail_msg )
2018-06-09 05:13:18 +00:00
# more tests for scalar (i.e. `sx == sy`) case
for curve in test_curves :
2018-05-22 22:48:45 +00:00
# scale by 2 around (100, 100)
2018-07-07 02:59:28 +00:00
scaled_curve = curve . scaled ( 2.0 , origin = complex ( 100 , 100 ) )
2018-05-22 22:48:45 +00:00
# expected length
2018-06-09 05:13:18 +00:00
len_orig = curve . length ( )
len_trns = scaled_curve . length ( )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( len_orig * 2.0 , len_trns , delta = TOL )
2018-05-22 22:48:45 +00:00
# expected positions
2018-06-09 05:13:18 +00:00
for T in np . linspace ( 0.0 , 1.0 , num = 100 ) :
pt_orig = curve . point ( T )
pt_trns = scaled_curve . point ( T )
2018-05-22 22:48:45 +00:00
pt_xpct = ( pt_orig - complex ( 100 , 100 ) ) * 2.0 + complex ( 100 , 100 )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( pt_xpct , pt_trns , delta = TOL )
2018-05-22 22:48:45 +00:00
# scale by 0.3 around (0, -100)
2021-06-28 05:30:56 +00:00
# the 'almost equal' test fails at the 7th decimal place for
2018-05-23 02:34:56 +00:00
# some length and position tests here.
2018-07-07 02:59:28 +00:00
scaled_curve = curve . scaled ( 0.3 , origin = complex ( 0 , - 100 ) )
2018-05-22 22:48:45 +00:00
# expected length
2018-06-09 05:13:18 +00:00
len_orig = curve . length ( )
len_trns = scaled_curve . length ( )
self . assertAlmostEqual ( len_orig * 0.3 , len_trns , delta = 0.000001 )
2018-05-22 22:48:45 +00:00
# expected positions
2018-06-09 05:13:18 +00:00
for T in np . linspace ( 0.0 , 1.0 , num = 100 ) :
pt_orig = curve . point ( T )
pt_trns = scaled_curve . point ( T )
2018-05-22 22:48:45 +00:00
pt_xpct = ( pt_orig - complex ( 0 , - 100 ) ) * 0.3 + complex ( 0 , - 100 )
2018-06-09 05:13:18 +00:00
self . assertAlmostEqual ( pt_xpct , pt_trns , delta = 0.000001 )
2020-06-20 01:43:29 +00:00
def test_d ( self ) :
# the following two path represent the same path but in absolute and relative forms
abs_s = ' M 38.0,130.0 C 37.0,132.0 38.0,136.0 40.0,137.0 L 85.0,161.0 C 87.0,162.0 91.0,162.0 93.0,160.0 L 127.0,133.0 C 129.0,131.0 129.0,128.0 127.0,126.0 L 80.0,70.0 C 78.0,67.0 75.0,68.0 74.0,70.0 Z '
rel_s = ' m 38.0,130.0 c -1.0,2.0 0.0,6.0 2.0,7.0 l 45.0,24.0 c 2.0,1.0 6.0,1.0 8.0,-1.0 l 34.0,-27.0 c 2.0,-2.0 2.0,-5.0 0.0,-7.0 l -47.0,-56.0 c -2.0,-3.0 -5.0,-2.0 -6.0,0.0 z '
path1 = parse_path ( abs_s )
path2 = parse_path ( rel_s )
self . assertEqual ( path1 . d ( use_closed_attrib = True ) , abs_s )
self . assertEqual ( path2 . d ( use_closed_attrib = True ) , abs_s )
self . assertEqual ( path1 . d ( use_closed_attrib = True , rel = True ) , rel_s )
self . assertEqual ( path2 . d ( use_closed_attrib = True , rel = True ) , rel_s )
2021-06-28 05:30:56 +00:00
2016-07-06 04:51:11 +00:00
class Test_ilength ( unittest . TestCase ) :
2017-03-01 07:04:37 +00:00
# See svgpathtools.notes.inv_arclength.py for information on how these
# test values were generated (using the .length() method).
##############################################################
def test_ilength_lines ( self ) :
2016-07-06 04:51:11 +00:00
l = Line ( 1 , 3 - 1 j )
nodall = Line ( 1 + 1 j , 1 + 1 j )
tests = [ ( l , 0.01 , 0.022360679774997897 ) ,
( l , 0.1 , 0.223606797749979 ) ,
( l , 0.5 , 1.118033988749895 ) ,
( l , 0.9 , 2.012461179749811 ) ,
( l , 0.99 , 2.213707297724792 ) ]
for ( l , t , s ) in tests :
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( l . ilength ( s ) , t , delta = TOL )
2016-07-06 04:51:11 +00:00
2017-03-01 07:04:37 +00:00
def test_ilength_quadratics ( self ) :
2016-07-06 04:51:11 +00:00
q1 = QuadraticBezier ( 200 + 300 j , 400 + 50 j , 600 + 300 j )
q2 = QuadraticBezier ( 200 + 300 j , 400 + 50 j , 500 + 200 j )
closedq = QuadraticBezier ( 6 + 2 j , 5 - 1 j , 6 + 2 j )
linq = QuadraticBezier ( 1 + 3 j , 2 + 5 j , - 9 - 17 j )
nodalq = QuadraticBezier ( 1 , 1 , 1 )
tests = [ ( q1 , 0.01 , 6.364183310105577 ) ,
( q1 , 0.1 , 60.23857499635088 ) ,
( q1 , 0.5 , 243.8855469477619 ) ,
( q1 , 0.9 , 427.53251889917294 ) ,
( q1 , 0.99 , 481.40691058541813 ) ,
( q2 , 0.01 , 6.365673533661836 ) ,
( q2 , 0.1 , 60.31675895732397 ) ,
( q2 , 0.5 , 233.24592830045907 ) ,
( q2 , 0.9 , 346.42891253298706 ) ,
( q2 , 0.99 , 376.32659156736844 ) ,
( closedq , 0.01 , 0.06261309767133393 ) ,
( closedq , 0.1 , 0.5692099788303084 ) ,
( closedq , 0.5 , 1.5811388300841898 ) ,
( closedq , 0.9 , 2.5930676813380713 ) ,
( closedq , 0.99 , 3.0996645624970456 ) ,
( linq , 0.01 , 0.04203807797699605 ) ,
( linq , 0.1 , 0.19379255804998186 ) ,
( linq , 0.5 , 4.844813951249544 ) ,
( linq , 0.9 , 18.0823363780483 ) ,
( linq , 0.99 , 22.24410609777091 ) ]
for q , t , s in tests :
try :
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( q . ilength ( s ) , t , delta = TOL )
2016-07-06 04:51:11 +00:00
except :
print ( q )
print ( s )
print ( t )
raise
2017-03-01 07:04:37 +00:00
def test_ilength_cubics ( self ) :
2016-07-06 04:51:11 +00:00
c1 = CubicBezier ( 200 + 300 j , 400 + 50 j , 600 + 100 j , - 200 )
symc = CubicBezier ( 1 - 2 j , 10 - 1 j , 10 + 1 j , 1 + 2 j )
closedc = CubicBezier ( 1 - 2 j , 10 - 1 j , 10 + 1 j , 1 - 2 j )
tests = [ ( c1 , 0.01 , 9.53434737943073 ) ,
( c1 , 0.1 , 88.89941848775852 ) ,
( c1 , 0.5 , 278.5750942713189 ) ,
( c1 , 0.9 , 651.4957786584646 ) ,
( c1 , 0.99 , 840.2010603832538 ) ,
( symc , 0.01 , 0.2690118556702902 ) ,
( symc , 0.1 , 2.45230693868727 ) ,
( symc , 0.5 , 7.256147083644424 ) ,
( symc , 0.9 , 12.059987228602886 ) ,
( symc , 0.99 , 14.243282311619401 ) ,
( closedc , 0.01 , 0.26901140075538765 ) ,
( closedc , 0.1 , 2.451722765460998 ) ,
( closedc , 0.5 , 6.974058969750422 ) ,
( closedc , 0.9 , 11.41781741489913 ) ,
( closedc , 0.99 , 13.681324783697782 ) ]
for ( c , t , s ) in tests :
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( c . ilength ( s ) , t , delta = TOL )
2016-07-06 04:51:11 +00:00
2017-03-01 07:04:37 +00:00
def test_ilength_arcs ( self ) :
2016-07-06 04:51:11 +00:00
arc1 = Arc ( 0 j , 100 + 50 j , 0 , 0 , 0 , 100 + 50 j )
arc2 = Arc ( 0 j , 100 + 50 j , 0 , 1 , 0 , 100 + 50 j )
arc3 = Arc ( 0 j , 100 + 50 j , 0 , 0 , 1 , 100 + 50 j )
arc4 = Arc ( 0 j , 100 + 50 j , 0 , 1 , 1 , 100 + 50 j )
arc5 = Arc ( 0 j , 100 + 100 j , 0 , 0 , 0 , 200 + 0 j )
arc6 = Arc ( 200 + 0 j , 100 + 100 j , 0 , 0 , 0 , 0 j )
arc7 = Arc ( 0 j , 100 + 50 j , 0 , 0 , 0 , 100 + 50 j )
tests = [ ( arc1 , 0.01 , 0.785495042476231 ) ,
( arc1 , 0.1 , 7.949362877455911 ) ,
( arc1 , 0.5 , 48.28318721111137 ) ,
( arc1 , 0.9 , 105.44598206942156 ) ,
( arc1 , 0.99 , 119.53485487631241 ) ,
( arc2 , 0.01 , 4.71108115728524 ) ,
( arc2 , 0.1 , 45.84152747676626 ) ,
( arc2 , 0.5 , 169.38878996795734 ) ,
( arc2 , 0.9 , 337.44707303579696 ) ,
( arc2 , 0.99 , 360.95800139278765 ) ,
( arc3 , 0.01 , 1.5707478805335624 ) ,
( arc3 , 0.1 , 15.659620687424416 ) ,
( arc3 , 0.5 , 72.82241554573457 ) ,
( arc3 , 0.9 , 113.15623987939003 ) ,
( arc3 , 0.99 , 120.3201077143697 ) ,
( arc4 , 0.01 , 2.3588068777503897 ) ,
( arc4 , 0.1 , 25.869735234740887 ) ,
( arc4 , 0.5 , 193.9280183025816 ) ,
( arc4 , 0.9 , 317.4752807937718 ) ,
( arc4 , 0.99 , 358.6057271132536 ) ,
( arc5 , 0.01 , 3.141592653589793 ) ,
( arc5 , 0.1 , 31.415926535897935 ) ,
( arc5 , 0.5 , 157.07963267948966 ) ,
( arc5 , 0.9 , 282.7433388230814 ) ,
( arc5 , 0.99 , 311.01767270538954 ) ,
( arc6 , 0.01 , 3.141592653589793 ) ,
( arc6 , 0.1 , 31.415926535897928 ) ,
( arc6 , 0.5 , 157.07963267948966 ) ,
( arc6 , 0.9 , 282.7433388230814 ) ,
( arc6 , 0.99 , 311.01767270538954 ) ,
( arc7 , 0.01 , 0.785495042476231 ) ,
( arc7 , 0.1 , 7.949362877455911 ) ,
( arc7 , 0.5 , 48.28318721111137 ) ,
( arc7 , 0.9 , 105.44598206942156 ) ,
( arc7 , 0.99 , 119.53485487631241 ) ]
for ( c , t , s ) in tests :
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( c . ilength ( s ) , t , delta = TOL )
2016-07-06 04:51:11 +00:00
2017-03-01 07:04:37 +00:00
def test_ilength_paths ( self ) :
2016-07-06 04:51:11 +00:00
line1 = Line ( 600 + 350 j , 650 + 325 j )
arc1 = Arc ( 650 + 325 j , 25 + 25 j , - 30 , 0 , 1 , 700 + 300 j )
cub1 = CubicBezier ( 650 + 325 j , 25 + 25 j , - 30 , 700 + 300 j )
cub2 = CubicBezier ( 700 + 300 j , 800 + 400 j , 750 + 200 j , 600 + 100 j )
quad3 = QuadraticBezier ( 600 + 100 j , 600 , 600 + 300 j )
linez = Line ( 600 + 300 j , 600 + 350 j )
bezpath = Path ( line1 , cub1 , cub2 , quad3 )
bezpathz = Path ( line1 , cub1 , cub2 , quad3 , linez )
path = Path ( line1 , arc1 , cub2 , quad3 )
pathz = Path ( line1 , arc1 , cub2 , quad3 , linez )
lpath = Path ( linez )
qpath = Path ( quad3 )
cpath = Path ( cub1 )
apath = Path ( arc1 )
tests = [ ( bezpath , 0.0 , 0.0 ) ,
( bezpath , 0.1111111111111111 , 286.2533595149515 ) ,
( bezpath , 0.2222222222222222 , 503.8620222915423 ) ,
( bezpath , 0.3333333333333333 , 592.6337135346268 ) ,
( bezpath , 0.4444444444444444 , 644.3880677233315 ) ,
( bezpath , 0.5555555555555556 , 835.0384185011363 ) ,
( bezpath , 0.6666666666666666 , 1172.8729938994575 ) ,
( bezpath , 0.7777777777777778 , 1308.6205983178952 ) ,
( bezpath , 0.8888888888888888 , 1532.8473168900994 ) ,
( bezpath , 1.0 , 1758.2427369258733 ) ,
( bezpathz , 0.0 , 0.0 ) ,
( bezpathz , 0.1111111111111111 , 294.15942308605435 ) ,
( bezpathz , 0.2222222222222222 , 512.4295461513882 ) ,
( bezpathz , 0.3333333333333333 , 594.0779370040138 ) ,
( bezpathz , 0.4444444444444444 , 658.7361976564598 ) ,
( bezpathz , 0.5555555555555556 , 874.1674336581542 ) ,
( bezpathz , 0.6666666666666666 , 1204.2371344392693 ) ,
( bezpathz , 0.7777777777777778 , 1356.773042865213 ) ,
( bezpathz , 0.8888888888888888 , 1541.808492602876 ) ,
( bezpathz , 1.0 , 1808.2427369258733 ) ,
( path , 0.0 , 0.0 ) ,
( path , 0.1111111111111111 , 81.44016397108298 ) ,
( path , 0.2222222222222222 , 164.72556816469307 ) ,
( path , 0.3333333333333333 , 206.71343564679154 ) ,
( path , 0.4444444444444444 , 265.4898349999353 ) ,
( path , 0.5555555555555556 , 367.5420981413199 ) ,
( path , 0.6666666666666666 , 487.29863861165995 ) ,
( path , 0.7777777777777778 , 511.84069655405284 ) ,
( path , 0.8888888888888888 , 579.9530841780238 ) ,
( path , 1.0 , 732.9614757397469 ) ,
( pathz , 0.0 , 0.0 ) ,
( pathz , 0.1111111111111111 , 86.99571952663854 ) ,
( pathz , 0.2222222222222222 , 174.33662608180325 ) ,
( pathz , 0.3333333333333333 , 214.42194393858466 ) ,
( pathz , 0.4444444444444444 , 289.94661033436205 ) ,
( pathz , 0.5555555555555556 , 408.38391100702125 ) ,
( pathz , 0.6666666666666666 , 504.4309373835351 ) ,
( pathz , 0.7777777777777778 , 533.774834546298 ) ,
( pathz , 0.8888888888888888 , 652.931321760894 ) ,
( pathz , 1.0 , 782.9614757397469 ) ,
( lpath , 0.0 , 0.0 ) ,
( lpath , 0.1111111111111111 , 5.555555555555555 ) ,
( lpath , 0.2222222222222222 , 11.11111111111111 ) ,
( lpath , 0.3333333333333333 , 16.666666666666664 ) ,
( lpath , 0.4444444444444444 , 22.22222222222222 ) ,
( lpath , 0.5555555555555556 , 27.77777777777778 ) ,
( lpath , 0.6666666666666666 , 33.33333333333333 ) ,
( lpath , 0.7777777777777778 , 38.88888888888889 ) ,
( lpath , 0.8888888888888888 , 44.44444444444444 ) ,
( lpath , 1.0 , 50.0 ) ,
( qpath , 0.0 , 0.0 ) ,
( qpath , 0.1111111111111111 , 17.28395061728395 ) ,
( qpath , 0.2222222222222222 , 24.69135802469136 ) ,
( qpath , 0.3333333333333333 , 27.777777777777786 ) ,
( qpath , 0.4444444444444444 , 40.12345679012344 ) ,
( qpath , 0.5555555555555556 , 62.3456790123457 ) ,
( qpath , 0.6666666666666666 , 94.44444444444446 ) ,
( qpath , 0.7777777777777778 , 136.41975308641975 ) ,
( qpath , 0.8888888888888888 , 188.27160493827154 ) ,
( qpath , 1.0 , 250.0 ) ,
( cpath , 0.0 , 0.0 ) ,
( cpath , 0.1111111111111111 , 207.35525375551356 ) ,
( cpath , 0.2222222222222222 , 366.0583590267552 ) ,
( cpath , 0.3333333333333333 , 474.34064293812787 ) ,
( cpath , 0.4444444444444444 , 530.467036317684 ) ,
( cpath , 0.5555555555555556 , 545.0444351253911 ) ,
( cpath , 0.6666666666666666 , 598.9767847757622 ) ,
( cpath , 0.7777777777777778 , 710.4080903390646 ) ,
( cpath , 0.8888888888888888 , 881.1796899225557 ) ,
( cpath , 1.0 , 1113.0914444911352 ) ,
( apath , 0.0 , 0.0 ) ,
( apath , 0.1111111111111111 , 9.756687033889872 ) ,
( apath , 0.2222222222222222 , 19.51337406777974 ) ,
( apath , 0.3333333333333333 , 29.27006110166961 ) ,
( apath , 0.4444444444444444 , 39.02674813555948 ) ,
( apath , 0.5555555555555556 , 48.783435169449355 ) ,
( apath , 0.6666666666666666 , 58.54012220333922 ) ,
( apath , 0.7777777777777778 , 68.2968092372291 ) ,
( apath , 0.8888888888888888 , 78.05349627111896 ) ,
( apath , 1.0 , 87.81018330500885 ) ]
for ( c , t , s ) in tests :
2018-07-10 01:40:00 +00:00
try :
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( c . ilength ( s ) , t , msg = str ( ( c , t , s ) ) , delta = TOL )
2018-07-10 01:40:00 +00:00
except :
# These test case values were generated using a system
2021-06-28 05:30:56 +00:00
# with scipy installed -- if scipy is not installed,
# then in cases where `t == 1`, `s` may be slightly
# greater than the length computed previously.
2018-07-10 01:40:00 +00:00
# Thus this try/except block exists as a workaround.
if c . length ( ) < s :
with self . assertRaises ( ValueError ) :
c . ilength ( s )
else :
raise
2016-07-06 04:51:11 +00:00
2017-03-01 07:04:37 +00:00
# Exceptional Cases
def test_ilength_exceptions ( self ) :
2016-07-06 04:51:11 +00:00
nodalq = QuadraticBezier ( 1 , 1 , 1 )
with self . assertRaises ( AssertionError ) :
nodalq . ilength ( 1 )
lin = Line ( 0 , 0.5 j )
with self . assertRaises ( ValueError ) :
lin . ilength ( 1 )
2017-02-21 06:57:59 +00:00
class Test_intersect ( unittest . TestCase ) :
def test_intersect ( self ) :
###################################################################
# test that `some_seg.intersect(another_seg)` will produce properly
# ordered tuples, i.e. the first element in each tuple refers to
# `some_seg` and the second element refers to `another_seg`.
# Also tests that the correct number of intersections is found.
a = Line ( 0 + 200 j , 300 + 200 j )
2017-03-01 02:29:19 +00:00
b = QuadraticBezier ( 40 + 150 j , 70 + 200 j , 210 + 300 j )
c = CubicBezier ( 60 + 150 j , 40 + 200 j , 120 + 250 j , 200 + 160 j )
2017-02-21 06:57:59 +00:00
d = Arc ( 70 + 150 j , 50 + 100 j , 0 , 0 , 0 , 200 + 100 j )
segdict = { ' line ' : a , " quadratic " : b , ' cubic ' : c , ' arc ' : d }
# test each segment type against each other type
for x , y in [ ( x , y ) for x in segdict for y in segdict ] :
if x == y :
continue
x = segdict [ x ]
y = segdict [ y ]
xiy = sorted ( x . intersect ( y , tol = 1e-15 ) )
yix = sorted ( y . intersect ( x , tol = 1e-15 ) , key = itemgetter ( 1 ) )
for xy , yx in zip ( xiy , yix ) :
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( xy [ 0 ] , yx [ 1 ] , delta = TOL )
self . assertAlmostEqual ( xy [ 1 ] , yx [ 0 ] , delta = TOL )
self . assertAlmostEqual ( x . point ( xy [ 0 ] ) , y . point ( yx [ 0 ] ) , delta = TOL )
2020-12-09 02:38:51 +00:00
self . assertTrue ( len ( xiy ) == len ( yix ) )
2017-02-21 06:57:59 +00:00
# test each segment against another segment of same type
for x in segdict :
if x == ' arc ' :
# this is an example of the Arc.intersect method not working
# in call cases. See docstring for a note on its
# incomplete implementation.
continue
x = segdict [ x ]
y = x . rotated ( 90 ) . translated ( 5 )
xiy = sorted ( x . intersect ( y , tol = 1e-15 ) )
yix = sorted ( y . intersect ( x , tol = 1e-15 ) , key = itemgetter ( 1 ) )
for xy , yx in zip ( xiy , yix ) :
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( xy [ 0 ] , yx [ 1 ] , delta = TOL )
self . assertAlmostEqual ( xy [ 1 ] , yx [ 0 ] , delta = TOL )
self . assertAlmostEqual ( x . point ( xy [ 0 ] ) , y . point ( yx [ 0 ] ) , delta = TOL )
2020-12-09 02:38:51 +00:00
self . assertTrue ( len ( xiy ) == len ( yix ) )
self . assertTrue ( len ( xiy ) == 1 )
self . assertTrue ( len ( yix ) == 1 )
2017-02-21 06:57:59 +00:00
###################################################################
2018-01-21 03:36:06 +00:00
def test_line_line_0 ( self ) :
2021-06-28 05:30:56 +00:00
l0 = Line ( start = ( 25.389999999999997 + 99.989999999999995 j ) ,
2018-05-23 02:34:56 +00:00
end = ( 25.389999999999997 + 90.484999999999999 j ) )
2021-06-28 05:30:56 +00:00
l1 = Line ( start = ( 25.390000000000001 + 84.114999999999995 j ) ,
2018-05-23 02:34:56 +00:00
end = ( 25.389999999999997 + 74.604202137430320 j ) )
2018-01-07 05:58:10 +00:00
i = l0 . intersect ( l1 )
assert ( len ( i ) ) == 0
2018-01-21 03:36:25 +00:00
def test_line_line_1 ( self ) :
2021-06-28 05:30:56 +00:00
l0 = Line ( start = ( - 124.705378549 + 327.696674827 j ) ,
2018-05-23 02:34:56 +00:00
end = ( 12.4926214511 + 121.261674827 j ) )
2021-06-28 05:30:56 +00:00
l1 = Line ( start = ( - 12.4926214511 + 121.261674827 j ) ,
2018-05-23 02:34:56 +00:00
end = ( 124.705378549 + 327.696674827 j ) )
2018-01-21 03:36:25 +00:00
i = l0 . intersect ( l1 )
assert ( len ( i ) ) == 1
assert ( abs ( l0 . point ( i [ 0 ] [ 0 ] ) - l1 . point ( i [ 0 ] [ 1 ] ) ) < 1e-9 )
2018-11-05 04:55:17 +00:00
def test_arc_line ( self ) :
l = Line ( start = ( - 20 + 1 j ) , end = ( 20 + 1 j ) )
a = Arc ( start = ( - 10 + 0 ) , radius = ( 10 + 10 j ) , rotation = 0.0 , large_arc = True , sweep = False , end = ( 10 + 0 j ) )
intersections = a . intersect ( l )
2021-06-28 05:30:56 +00:00
assert_intersections ( self , a , l , intersections , 2 )
2018-11-05 04:55:17 +00:00
l = Line ( start = ( - 20 - 1 j ) , end = ( 20 - 1 j ) )
a = Arc ( start = ( - 10 + 0 ) , radius = ( 10 + 10 j ) , rotation = 0.0 , large_arc = True , sweep = False , end = ( 10 + 0 j ) )
intersections = a . intersect ( l )
2021-06-28 05:30:56 +00:00
assert_intersections ( self , a , l , intersections , 0 )
2018-11-05 04:55:17 +00:00
l = Line ( start = ( - 20 + 1 j ) , end = ( 20 + 1 j ) )
a = Arc ( start = ( - 10 + 0 ) , radius = ( 10 + 10 j ) , rotation = 0.0 , large_arc = True , sweep = True , end = ( 10 + 0 j ) )
intersections = a . intersect ( l )
2021-06-28 05:30:56 +00:00
assert_intersections ( self , a , l , intersections , 0 )
2018-11-05 04:55:17 +00:00
l = Line ( start = ( - 20 - 1 j ) , end = ( 20 - 1 j ) )
a = Arc ( start = ( - 10 + 0 ) , radius = ( 10 + 10 j ) , rotation = 0.0 , large_arc = True , sweep = True , end = ( 10 + 0 j ) )
intersections = a . intersect ( l )
2021-06-28 05:30:56 +00:00
assert_intersections ( self , a , l , intersections , 2 )
2018-11-05 04:55:17 +00:00
l = Line ( start = ( - 20 + 0 j ) , end = ( 20 + 0 j ) )
a = Arc ( start = ( - 10 + 0 ) , radius = ( 10 + 10 j ) , rotation = 0.0 , large_arc = True , sweep = True , end = ( 10 + 0 j ) )
intersections = a . intersect ( l )
2021-06-28 05:30:56 +00:00
assert_intersections ( self , a , l , intersections , 2 )
2018-11-05 04:55:17 +00:00
l = Line ( start = ( - 20 + 0 j ) , end = ( 20 + 0 j ) )
a = Arc ( start = ( - 10 + 0 ) , radius = ( 10 + 10 j ) , rotation = 0.0 , large_arc = True , sweep = False , end = ( 10 + 0 j ) )
intersections = a . intersect ( l )
2021-06-28 05:30:56 +00:00
assert_intersections ( self , a , l , intersections , 2 )
2018-11-05 04:55:17 +00:00
l = Line ( start = ( - 20 + 10 j ) , end = ( 20 + 10 j ) )
a = Arc ( start = ( - 10 + 0 ) , radius = ( 10 + 10 j ) , rotation = 0.0 , large_arc = True , sweep = False , end = ( 10 + 0 j ) )
intersections = a . intersect ( l )
2021-06-28 05:30:56 +00:00
assert_intersections ( self , a , l , intersections , 1 )
2018-11-05 04:55:17 +00:00
l = Line ( start = ( 229.226097475 - 282.403591377 j ) , end = ( 751.681212592 + 188.907748894 j ) )
a = Arc ( start = ( - 1 - 750 j ) , radius = ( 750 + 750 j ) , rotation = 0.0 , large_arc = True , sweep = False , end = ( 1 - 750 j ) )
intersections = a . intersect ( l )
2021-06-28 05:30:56 +00:00
assert_intersections ( self , a , l , intersections , 1 )
2018-11-05 04:55:17 +00:00
# end of arc touches start of horizontal line
l = Line ( start = ( 40.234 - 32.613 j ) , end = ( 12.7 - 32.613 j ) )
a = Arc ( start = ( 100.834 + 27.987 j ) , radius = ( 60.6 + 60.6 j ) , rotation = 0.0 , large_arc = False , sweep = False , end = ( 40.234 - 32.613 j ) )
intersections = a . intersect ( l )
2021-06-28 05:30:56 +00:00
assert_intersections ( self , a , l , intersections , 1 )
2018-11-05 04:55:17 +00:00
# vertical line, intersects half-arc once
l = Line ( start = ( 1 - 100 j ) , end = ( 1 + 100 j ) )
a = Arc ( start = ( 10.0 + 0 j ) , radius = ( 10 + 10 j ) , rotation = 0 , large_arc = False , sweep = True , end = ( - 10.0 + 0 j ) )
intersections = a . intersect ( l )
2021-06-28 05:30:56 +00:00
assert_intersections ( self , a , l , intersections , 1 )
2018-11-05 04:55:17 +00:00
# vertical line, intersects nearly-full arc twice
l = Line ( start = ( 1 - 100 j ) , end = ( 1 + 100 j ) )
a = Arc ( start = ( 0.1 - 10 j ) , radius = ( 10 + 10 j ) , rotation = 0 , large_arc = True , sweep = True , end = ( - 0.1 - 10 j ) )
intersections = a . intersect ( l )
2021-06-28 05:30:56 +00:00
assert_intersections ( self , a , l , intersections , 2 )
2018-11-05 04:55:17 +00:00
# vertical line, start of line touches end of arc
l = Line ( start = ( 15.4 + 100 j ) , end = ( 15.4 + 90.475 j ) )
a = Arc ( start = ( 25.4 + 90 j ) , radius = ( 10 + 10 j ) , rotation = 0 , large_arc = False , sweep = True , end = ( 15.4 + 100 j ) )
intersections = a . intersect ( l )
2021-06-28 05:30:56 +00:00
assert_intersections ( self , a , l , intersections , 1 )
2018-11-05 04:55:17 +00:00
l = Line ( start = ( 100 - 60.913 j ) , end = ( 40 + 59 j ) )
a = Arc ( start = ( 100.834 + 27.987 j ) , radius = ( 60.6 + 60.6 j ) , rotation = 0.0 , large_arc = False , sweep = False , end = ( 40.234 - 32.613 j ) )
intersections = a . intersect ( l )
2021-06-28 05:30:56 +00:00
assert_intersections ( self , a , l , intersections , 1 )
2018-11-05 04:55:17 +00:00
l = Line ( start = ( 128.57143 + 380.93364 j ) , end = ( 300.00001 + 389.505069 j ) )
a = Arc ( start = ( 214.28572 + 598.07649 j ) , radius = ( 85.714287 + 108.57143 j ) , rotation = 0.0 , large_arc = False , sweep = True , end = ( 128.57143 + 489.50507 j ) )
intersections = a . intersect ( l )
2021-06-28 05:30:56 +00:00
assert_intersections ( self , a , l , intersections , 0 )
2018-11-05 04:55:17 +00:00
random . seed ( )
for arc_index in range ( 50 ) :
a = random_arc ( )
for line_index in range ( 100 ) :
l = random_line ( )
intersections = a . intersect ( l )
2021-06-28 05:30:56 +00:00
msg = ' Generated: arc = {} , line = {} ' . format ( a , l )
assert_intersections ( self , a , l , intersections , None , msg = msg )
2018-11-05 04:55:17 +00:00
def test_intersect_arc_line_1 ( self ) :
""" Verify the return value of intersects() when an Arc ends at
the starting point of a Line . """
a = Arc ( start = ( 0 + 0 j ) , radius = ( 10 + 10 j ) , rotation = 0 , large_arc = False ,
sweep = False , end = ( 10 + 10 j ) , autoscale_radius = False )
l = Line ( start = ( 10 + 10 j ) , end = ( 20 + 10 j ) )
i = a . intersect ( l )
self . assertEqual ( len ( i ) , 1 )
self . assertEqual ( i [ 0 ] [ 0 ] , 1.0 )
self . assertEqual ( i [ 0 ] [ 1 ] , 0.0 )
def test_intersect_arc_line_2 ( self ) :
""" Verify the return value of intersects() when an Arc is pierced
once by a Line . """
a = Arc ( start = ( 0 + 0 j ) , radius = ( 10 + 10 j ) , rotation = 0 , large_arc = False ,
sweep = False , end = ( 10 + 10 j ) , autoscale_radius = False )
l = Line ( start = ( 0 + 9 j ) , end = ( 20 + 9 j ) )
i = a . intersect ( l )
self . assertEqual ( len ( i ) , 1 )
self . assertGreaterEqual ( i [ 0 ] [ 0 ] , 0.0 )
self . assertLessEqual ( i [ 0 ] [ 0 ] , 1.0 )
self . assertGreaterEqual ( i [ 0 ] [ 1 ] , 0.0 )
self . assertLessEqual ( i [ 0 ] [ 1 ] , 1.0 )
def test_intersect_arc_line_3 ( self ) :
""" Verify the return value of intersects() when an Arc misses
a Line , but the circle that the Arc is part of hits the Line . """
a = Arc ( start = ( 0 + 0 j ) , radius = ( 10 + 10 j ) , rotation = 0 , large_arc = False ,
sweep = False , end = ( 10 + 10 j ) , autoscale_radius = False )
l = Line ( start = ( 11 + 100 j ) , end = ( 11 - 100 j ) )
i = a . intersect ( l )
self . assertEqual ( len ( i ) , 0 )
def test_intersect_arc_line_disjoint_bboxes ( self ) :
# The arc is very short, which contributes to the problem here.
l = Line ( start = ( 125.314540561 + 144.192926144 j ) , end = ( 125.798713132 + 144.510685287 j ) )
a = Arc ( start = ( 128.26640649 + 146.908463323 j ) , radius = ( 2 + 2 j ) ,
rotation = 0 , large_arc = False , sweep = True ,
end = ( 128.26640606 + 146.90846449 j ) )
i = l . intersect ( a )
self . assertEqual ( i , [ ] )
2020-06-20 02:40:38 +00:00
def test_arc_arc_0 ( self ) :
# These arcs cross at a single point.
a0 = Arc ( start = ( 114.648 + 27.4280898219 j ) , radius = ( 22 + 22 j ) , rotation = 0 , large_arc = False , sweep = True , end = ( 118.542 + 39.925 j ) )
a1 = Arc ( start = ( 118.542 + 15.795 j ) , radius = ( 22 + 22 j ) , rotation = 0 , large_arc = False , sweep = True , end = ( 96.542 + 37.795 j ) )
intersections = a0 . intersect ( a1 )
2021-06-28 05:30:56 +00:00
assert_intersections ( self , a0 , a1 , intersections , 1 )
2020-06-20 02:40:38 +00:00
def test_arc_arc_1 ( self ) :
# These touch at an endpoint, and are *nearly* segments of a larger arc.
a0 = Arc ( start = ( - 12.8272110776 + 72.6464538932 j ) , radius = ( 44.029 + 44.029 j ) , rotation = 0.0 , large_arc = False , sweep = False , end = ( - 60.6807543328 + 75.3104334473 j ) )
a1 = Arc ( start = ( - 60.6807101078 + 75.3104011248 j ) , radius = ( 44.029 + 44.029 j ) , rotation = 0.0 , large_arc = False , sweep = False , end = ( - 77.7490636234 + 120.096609353 j ) )
intersections = a0 . intersect ( a1 )
2021-06-28 05:30:56 +00:00
assert_intersections ( self , a0 , a1 , intersections , 1 )
2020-06-20 02:40:38 +00:00
def test_arc_arc_2 ( self ) :
# These arcs cross at a single point.
a0 = Arc ( start = ( 112.648 + 5 j ) , radius = ( 24 + 24 j ) , rotation = 0 , large_arc = False , sweep = True , end = ( 136.648 + 29 j ) )
a1 = Arc ( start = ( 112.648 + 6.33538520071 j ) , radius = ( 24 + 24 j ) , rotation = 0 , large_arc = False , sweep = True , end = ( 120.542 + 5 j ) )
intersections = a0 . intersect ( a1 )
2021-06-28 05:30:56 +00:00
assert_intersections ( self , a0 , a1 , intersections , 1 )
2020-06-20 02:40:38 +00:00
# The Arcs in this test are part of the same circle.
def test_arc_arc_same_circle ( self ) :
# These touch at one endpoint, and go in the same direction.
a0 = Arc ( start = ( 0 + 0 j ) , radius = ( 10 + 10 j ) , rotation = 0.0 , large_arc = False , sweep = False , end = ( - 10 + 10 j ) )
a1 = Arc ( start = ( - 10 + 10 j ) , radius = ( 10 + 10 j ) , rotation = 0.0 , large_arc = False , sweep = False , end = ( 0 + 20 j ) )
intersections = a0 . intersect ( a1 )
2021-06-28 05:30:56 +00:00
assert_intersections ( self , a0 , a1 , intersections , 1 )
2020-06-20 02:40:38 +00:00
# These touch at both endpoints, and go in the same direction.
a0 = Arc ( start = ( 0 + 0 j ) , radius = ( 10 + 10 j ) , rotation = 0.0 , large_arc = False , sweep = False , end = ( - 10 + 10 j ) )
a1 = Arc ( start = ( - 10 + 10 j ) , radius = ( 10 + 10 j ) , rotation = 0.0 , large_arc = True , sweep = False , end = ( 0 + 0 j ) )
intersections = a0 . intersect ( a1 )
2021-06-28 05:30:56 +00:00
assert_intersections ( self , a0 , a1 , intersections , 2 )
2020-06-20 02:40:38 +00:00
# These touch at one endpoint, and go in opposite directions.
a0 = Arc ( start = ( 0 + 0 j ) , radius = ( 10 + 10 j ) , rotation = 0.0 , large_arc = False , sweep = False , end = ( 0 + 20 j ) )
a1 = Arc ( start = ( 0 + 20 j ) , radius = ( 10 + 10 j ) , rotation = 0.0 , large_arc = False , sweep = True , end = ( - 10 + 10 j ) )
intersections = a0 . intersect ( a1 )
2021-06-28 05:30:56 +00:00
assert_intersections ( self , a0 , a1 , intersections , 0 )
2020-06-20 02:40:38 +00:00
# These touch at both endpoints, and go in opposite directions.
a0 = Arc ( start = ( 0 + 0 j ) , radius = ( 10 + 10 j ) , rotation = 0.0 , large_arc = False , sweep = False , end = ( - 10 + 10 j ) )
a1 = Arc ( start = ( - 10 + 10 j ) , radius = ( 10 + 10 j ) , rotation = 0.0 , large_arc = False , sweep = True , end = ( 0 + 0 j ) )
intersections = a0 . intersect ( a1 )
2021-06-28 05:30:56 +00:00
assert_intersections ( self , a0 , a1 , intersections , 0 )
2020-06-20 02:40:38 +00:00
# These are totally disjoint.
a0 = Arc ( start = ( 0 + 0 j ) , radius = ( 10 + 10 j ) , rotation = 0.0 , large_arc = False , sweep = False , end = ( - 10 + 10 j ) )
a1 = Arc ( start = ( 0 + 20 j ) , radius = ( 10 + 10 j ) , rotation = 0.0 , large_arc = False , sweep = False , end = ( 10 + 10 j ) )
intersections = a0 . intersect ( a1 )
2021-06-28 05:30:56 +00:00
assert_intersections ( self , a0 , a1 , intersections , 0 )
2020-06-20 02:40:38 +00:00
# These overlap at one end and don't touch at the other.
a0 = Arc ( start = ( 0 + 0 j ) , radius = ( 10 + 10 j ) , rotation = 0.0 , large_arc = False , sweep = False , end = ( 0 + 20 j ) )
a1 = Arc ( start = ( - 10 + 10 j ) , radius = ( 10 + 10 j ) , rotation = 0.0 , large_arc = False , sweep = False , end = ( 10 + 10 j ) )
intersections = a0 . intersect ( a1 )
2021-06-28 05:30:56 +00:00
assert_intersections ( self , a0 , a1 , intersections , 0 )
2020-06-20 02:40:38 +00:00
# These overlap at one end and touch at the other.
a0 = Arc ( start = ( 0 + 0 j ) , radius = ( 10 + 10 j ) , rotation = 0.0 , large_arc = False , sweep = False , end = ( 0 + 20 j ) )
a1 = Arc ( start = ( - 10 + 10 j ) , radius = ( 10 + 10 j ) , rotation = 0.0 , large_arc = True , sweep = False , end = ( 0 + 0 j ) )
intersections = a0 . intersect ( a1 )
2021-06-28 05:30:56 +00:00
assert_intersections ( self , a0 , a1 , intersections , 0 )
2020-06-20 02:40:38 +00:00
# The Arcs in this test are part of tangent circles, outside each other.
def test_arc_arc_tangent_circles_outside ( self ) :
a0 = Arc ( start = ( 0 + 0 j ) , radius = ( 10 + 10 j ) , rotation = 0.0 , large_arc = False , sweep = False , end = ( 0 + 20 j ) )
a1 = Arc ( start = ( - 20 + 0 j ) , radius = ( 10 + 10 j ) , rotation = 0.0 , large_arc = False , sweep = True , end = ( - 20 + 20 j ) )
intersections = a0 . intersect ( a1 )
2021-06-28 05:30:56 +00:00
assert_intersections ( self , a0 , a1 , intersections , 1 )
2020-06-20 02:40:38 +00:00
a0 = Arc ( start = ( 0 + 0 j ) , radius = ( 10 + 10 j ) , rotation = 0.0 , large_arc = False , sweep = False , end = ( 0 + 20 j ) )
a1 = Arc ( start = ( - 20 + 0 j ) , radius = ( 10 + 10 j ) , rotation = 0.0 , large_arc = False , sweep = False , end = ( - 20 + 20 j ) )
intersections = a0 . intersect ( a1 )
2021-06-28 05:30:56 +00:00
assert_intersections ( self , a0 , a1 , intersections , 0 )
2020-06-20 02:40:38 +00:00
a0 = Arc ( start = ( 10 - 10 j ) , radius = ( 10 + 10 j ) , rotation = 0.0 , large_arc = False , sweep = False , end = ( 10 + 10 j ) )
a1 = Arc ( start = ( - 10 - 0 j ) , radius = ( 5 + 5 j ) , rotation = 0.0 , large_arc = True , sweep = True , end = ( - 5 + 5 j ) )
intersections = a0 . intersect ( a1 )
2021-06-28 05:30:56 +00:00
assert_intersections ( self , a0 , a1 , intersections , 1 )
2020-06-20 02:40:38 +00:00
# The Arcs in this test are part of tangent circles, one inside the other.
def test_arc_arc_tangent_circles_inside ( self ) :
a0 = Arc ( start = ( 10 - 10 j ) , radius = ( 10 + 10 j ) , rotation = 0.0 , large_arc = False , sweep = False , end = ( 10 + 10 j ) )
a1 = Arc ( start = ( 10 - 0 j ) , radius = ( 5 + 5 j ) , rotation = 0.0 , large_arc = True , sweep = True , end = ( 5 + 5 j ) )
intersections = a0 . intersect ( a1 )
2021-06-28 05:30:56 +00:00
assert_intersections ( self , a0 , a1 , intersections , 1 )
2020-06-20 02:40:38 +00:00
a0 = Arc ( start = ( 10 - 10 j ) , radius = ( 10 + 10 j ) , rotation = 0.0 , large_arc = False , sweep = False , end = ( 10 + 10 j ) )
a1 = Arc ( start = ( 10 - 0 j ) , radius = ( 5 + 5 j ) , rotation = 0.0 , large_arc = True , sweep = False , end = ( 5 + 5 j ) )
intersections = a0 . intersect ( a1 )
2021-06-28 05:30:56 +00:00
assert_intersections ( self , a0 , a1 , intersections , 1 )
2020-06-20 02:40:38 +00:00
a0 = Arc ( start = ( 10 - 10 j ) , radius = ( 10 + 10 j ) , rotation = 0.0 , large_arc = False , sweep = False , end = ( 10 + 10 j ) )
a1 = Arc ( start = ( 10 - 0 j ) , radius = ( 5 + 5 j ) , rotation = 0.0 , large_arc = False , sweep = False , end = ( 5 + 5 j ) )
intersections = a0 . intersect ( a1 )
2021-06-28 05:30:56 +00:00
assert_intersections ( self , a0 , a1 , intersections , 0 )
2020-06-20 02:40:38 +00:00
2017-03-24 07:18:22 +00:00
class TestPathTools ( unittest . TestCase ) :
# moved from test_pathtools.py
def setUp ( self ) :
self . arc1 = Arc ( 650 + 325 j , 25 + 25 j , - 30.0 , False , True , 700 + 300 j )
self . line1 = Line ( 0 , 100 + 100 j )
self . quadratic1 = QuadraticBezier ( 100 + 100 j , 150 + 150 j , 300 + 200 j )
self . cubic1 = CubicBezier ( 300 + 200 j , 350 + 400 j , 400 + 425 j , 650 + 325 j )
self . path_of_all_seg_types = Path ( self . line1 , self . quadratic1 ,
self . cubic1 , self . arc1 )
self . path_of_bezier_seg_types = Path ( self . line1 , self . quadratic1 ,
self . cubic1 )
def test_is_bezier_segment ( self ) :
# False
self . assertFalse ( is_bezier_segment ( self . arc1 ) )
self . assertFalse ( is_bezier_segment ( self . path_of_bezier_seg_types ) )
# True
self . assertTrue ( is_bezier_segment ( self . line1 ) )
self . assertTrue ( is_bezier_segment ( self . quadratic1 ) )
self . assertTrue ( is_bezier_segment ( self . cubic1 ) )
def test_is_bezier_path ( self ) :
# False
self . assertFalse ( is_bezier_path ( self . path_of_all_seg_types ) )
self . assertFalse ( is_bezier_path ( self . line1 ) )
self . assertFalse ( is_bezier_path ( self . quadratic1 ) )
self . assertFalse ( is_bezier_path ( self . cubic1 ) )
self . assertFalse ( is_bezier_path ( self . arc1 ) )
# True
self . assertTrue ( is_bezier_path ( self . path_of_bezier_seg_types ) )
self . assertTrue ( is_bezier_path ( Path ( ) ) )
def test_polynomial2bezier ( self ) :
def distfcn ( tup1 , tup2 ) :
assert len ( tup1 ) == len ( tup2 )
return sum ( ( tup1 [ i ] - tup2 [ i ] ) * * 2 for i in range ( len ( tup1 ) ) ) * * 0.5
# Case: Line
pcoeffs = [ ( - 1.7 - 2 j ) , ( 6 + 2 j ) ]
2018-06-09 05:13:18 +00:00
p = np . poly1d ( pcoeffs )
2017-03-24 07:18:22 +00:00
correct_bpoints = [ ( 6 + 2 j ) , ( 4.3 + 0 j ) ]
2018-06-09 05:13:18 +00:00
# Input np.poly1d object
2017-03-24 07:18:22 +00:00
bez = poly2bez ( p )
bpoints = bez . bpoints ( )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( distfcn ( bpoints , correct_bpoints ) , 0 , delta = TOL )
2017-03-24 07:18:22 +00:00
# Input list of coefficients
bpoints = poly2bez ( pcoeffs , return_bpoints = True )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( distfcn ( bpoints , correct_bpoints ) , 0 , delta = TOL )
2017-03-24 07:18:22 +00:00
# Case: Quadratic
pcoeffs = [ ( 29.5 + 15.5 j ) , ( - 31 - 19 j ) , ( 7.5 + 5.5 j ) ]
2018-06-09 05:13:18 +00:00
p = np . poly1d ( pcoeffs )
2017-03-24 07:18:22 +00:00
correct_bpoints = [ ( 7.5 + 5.5 j ) , ( - 8 - 4 j ) , ( 6 + 2 j ) ]
2018-06-09 05:13:18 +00:00
# Input np.poly1d object
2017-03-24 07:18:22 +00:00
bez = poly2bez ( p )
bpoints = bez . bpoints ( )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( distfcn ( bpoints , correct_bpoints ) , 0 , delta = TOL )
2017-03-24 07:18:22 +00:00
# Input list of coefficients
bpoints = poly2bez ( pcoeffs , return_bpoints = True )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( distfcn ( bpoints , correct_bpoints ) , 0 , delta = TOL )
2017-03-24 07:18:22 +00:00
# Case: Cubic
pcoeffs = [ ( - 18.5 - 12.5 j ) , ( 34.5 + 16.5 j ) , ( - 18 - 6 j ) , ( 6 + 2 j ) ]
2018-06-09 05:13:18 +00:00
p = np . poly1d ( pcoeffs )
2017-03-24 07:18:22 +00:00
correct_bpoints = [ ( 6 + 2 j ) , 0 j , ( 5.5 + 3.5 j ) , ( 4 + 0 j ) ]
2018-06-09 05:13:18 +00:00
# Input np.poly1d object
2017-03-24 07:18:22 +00:00
bez = poly2bez ( p )
bpoints = bez . bpoints ( )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( distfcn ( bpoints , correct_bpoints ) , 0 , delta = TOL )
2017-03-24 07:18:22 +00:00
# Input list of coefficients object
bpoints = poly2bez ( pcoeffs , return_bpoints = True )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( distfcn ( bpoints , correct_bpoints ) , 0 , delta = TOL )
2017-03-24 07:18:22 +00:00
def test_bpoints2bezier ( self ) :
cubic_bpoints = [ ( 6 + 2 j ) , 0 , ( 5.5 + 3.5 j ) , ( 4 + 0 j ) ]
quadratic_bpoints = [ ( 6 + 2 j ) , 0 , ( 5.5 + 3.5 j ) ]
line_bpoints = [ ( 6 + 2 j ) , 0 ]
self . assertTrue ( isinstance ( bpoints2bezier ( cubic_bpoints ) , CubicBezier ) )
self . assertTrue ( isinstance ( bpoints2bezier ( quadratic_bpoints ) ,
QuadraticBezier ) )
self . assertTrue ( isinstance ( bpoints2bezier ( line_bpoints ) , Line ) )
self . assertSequenceEqual ( bpoints2bezier ( cubic_bpoints ) . bpoints ( ) ,
cubic_bpoints )
self . assertSequenceEqual ( bpoints2bezier ( quadratic_bpoints ) . bpoints ( ) ,
quadratic_bpoints )
self . assertSequenceEqual ( bpoints2bezier ( line_bpoints ) . bpoints ( ) ,
line_bpoints )
# def test_line2pathd(self):
# bpoints = (0+1.5j, 100+10j)
# line = Line(*bpoints)
#
# # from Line object
# pathd = line2pathd(line)
# path = parse_path(pathd)
# self.assertTrue(path[0] == line)
#
# # from list of bpoints
# pathd = line2pathd(bpoints)
# path = parse_path(pathd)
# self.assertTrue(path[0] == line)
#
# def test_cubic2pathd(self):
# bpoints = (0+1.5j, 100+10j, 150-155.3j, 0)
# cubic = CubicBezier(*bpoints)
#
# # from Line object
# pathd = cubic2pathd(cubic)
# path = parse_path(pathd)
# self.assertTrue(path[0] == cubic)
#
# # from list of bpoints
# pathd = cubic2pathd(bpoints)
# path = parse_path(pathd)
# self.assertTrue(path[0] == cubic)
def test_closest_point_in_path ( self ) :
def distfcn ( tup1 , tup2 ) :
assert len ( tup1 ) == len ( tup2 )
return sum ( ( tup1 [ i ] - tup2 [ i ] ) * * 2 for i in range ( len ( tup1 ) ) ) * * 0.5
# Note: currently the radiialrange method is not implemented for Arc
# objects
# test_path = self.path_of_all_seg_types
# origin = -123 - 123j
# expected_result = ???
# self.assertAlmostEqual(min_radius(origin, test_path),
# expected_result)
# generic case (where is_bezier_path(test_path) == True)
test_path = self . path_of_bezier_seg_types
pt = 300 + 300 j
expected_result = ( 29.382522853493143 , 0.17477067969145446 , 2 )
result = closest_point_in_path ( pt , test_path )
err = distfcn ( expected_result , result )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( err , 0 , delta = TOL )
2017-03-24 07:18:22 +00:00
# cubic test with multiple valid solutions
test_path = Path ( CubicBezier ( 1 - 2 j , 10 - 1 j , 10 + 1 j , 1 + 2 j ) )
pt = 3
expected_results = [ ( 1.7191878932122302 , 0.90731678233211366 , 0 ) ,
( 1.7191878932122304 , 0.092683217667886342 , 0 ) ]
result = closest_point_in_path ( pt , test_path )
err = min ( distfcn ( e_res , result ) for e_res in expected_results )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( err , 0 , delta = TOL )
2017-03-24 07:18:22 +00:00
def test_farthest_point_in_path ( self ) :
def distfcn ( tup1 , tup2 ) :
assert len ( tup1 ) == len ( tup2 )
return sum ( ( tup1 [ i ] - tup2 [ i ] ) * * 2 for i in range ( len ( tup1 ) ) ) * * 0.5
# Note: currently the radiialrange method is not implemented for Arc
# objects
# test_path = self.path_of_all_seg_types
# origin = -123 - 123j
# expected_result = ???
# self.assertAlmostEqual(min_radius(origin, test_path),
# expected_result)
# boundary test
test_path = self . path_of_bezier_seg_types
pt = 300 + 300 j
expected_result = ( 424.26406871192853 , 0 , 0 )
result = farthest_point_in_path ( pt , test_path )
err = distfcn ( expected_result , result )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( err , 0 , delta = TOL )
2017-03-24 07:18:22 +00:00
# non-boundary test
test_path = Path ( CubicBezier ( 1 - 2 j , 10 - 1 j , 10 + 1 j , 1 + 2 j ) )
pt = 3
expected_result = ( 4.75 , 0.5 , 0 )
result = farthest_point_in_path ( pt , test_path )
err = distfcn ( expected_result , result )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( err , 0 , delta = TOL )
2017-03-24 07:18:22 +00:00
def test_path_encloses_pt ( self ) :
line1 = Line ( 0 , 100 + 100 j )
quadratic1 = QuadraticBezier ( 100 + 100 j , 150 + 150 j , 300 + 200 j )
cubic1 = CubicBezier ( 300 + 200 j , 350 + 400 j , 400 + 425 j , 650 + 325 j )
line2 = Line ( 650 + 325 j , 650 + 10 j )
line3 = Line ( 650 + 10 j , 0 )
open_bez_path = Path ( line1 , quadratic1 , cubic1 )
closed_bez_path = Path ( line1 , quadratic1 , cubic1 , line2 , line3 )
inside_pt = 200 + 20 j
outside_pt1 = 1000 + 1000 j
outside_pt2 = 800 + 800 j
boundary_pt = 50 + 50 j
# Note: currently the intersect() method is not implemented for Arc
# objects
# arc1 = Arc(650+325j, 25+25j, -30.0, False, True, 700+300j)
# closed_path_with_arc = Path(line1, quadratic1, cubic1, arc1)
# self.assertTrue(
# path_encloses_pt(inside_pt, outside_pt2, closed_path_with_arc))
# True cases
self . assertTrue (
path_encloses_pt ( inside_pt , outside_pt2 , closed_bez_path ) )
self . assertTrue (
path_encloses_pt ( boundary_pt , outside_pt2 , closed_bez_path ) )
# False cases
self . assertFalse (
path_encloses_pt ( outside_pt1 , outside_pt2 , closed_bez_path ) )
# Exception Cases
with self . assertRaises ( AssertionError ) :
path_encloses_pt ( inside_pt , outside_pt2 , open_bez_path )
# Display test paths and points
# ns2d = [inside_pt, outside_pt1, outside_pt2, boundary_pt]
# ncolors = ['green', 'red', 'orange', 'purple']
# disvg(closed_path_with_arc, nodes=ns2d, node_colors=ncolors,
# openinbrowser=True)
# disvg(open_bez_path, nodes=ns2d, node_colors=ncolors,
# openinbrowser=True)
# disvg(closed_bez_path, nodes=ns2d, node_colors=ncolors,
# openinbrowser=True)
2018-11-05 05:40:56 +00:00
def test_path_area ( self ) :
2021-06-28 05:30:56 +00:00
if not RUN_SLOW_TESTS :
warnings . warn ( " Skipping `test_path_area` as RUN_SLOW_TESTS is false. " )
return
2018-11-05 05:40:56 +00:00
cw_square = Path ( )
cw_square . append ( Line ( ( 0 + 0 j ) , ( 0 + 100 j ) ) )
cw_square . append ( Line ( ( 0 + 100 j ) , ( 100 + 100 j ) ) )
cw_square . append ( Line ( ( 100 + 100 j ) , ( 100 + 0 j ) ) )
cw_square . append ( Line ( ( 100 + 0 j ) , ( 0 + 0 j ) ) )
self . assertEqual ( cw_square . area ( ) , - 10000.0 )
ccw_square = Path ( )
ccw_square . append ( Line ( ( 0 + 0 j ) , ( 100 + 0 j ) ) )
ccw_square . append ( Line ( ( 100 + 0 j ) , ( 100 + 100 j ) ) )
ccw_square . append ( Line ( ( 100 + 100 j ) , ( 0 + 100 j ) ) )
ccw_square . append ( Line ( ( 0 + 100 j ) , ( 0 + 0 j ) ) )
self . assertEqual ( ccw_square . area ( ) , 10000.0 )
cw_half_circle = Path ( )
cw_half_circle . append ( Line ( ( 0 + 0 j ) , ( 0 + 100 j ) ) )
cw_half_circle . append ( Arc ( start = ( 0 + 100 j ) , radius = ( 50 + 50 j ) , rotation = 0 , large_arc = False , sweep = False , end = ( 0 + 0 j ) ) )
self . assertAlmostEqual ( cw_half_circle . area ( ) , - 3926.9908169872415 , places = 3 )
self . assertAlmostEqual ( cw_half_circle . area ( chord_length = 1e-3 ) , - 3926.9908169872415 , places = 6 )
ccw_half_circle = Path ( )
ccw_half_circle . append ( Line ( ( 0 + 100 j ) , ( 0 + 0 j ) ) )
ccw_half_circle . append ( Arc ( start = ( 0 + 0 j ) , radius = ( 50 + 50 j ) , rotation = 0 , large_arc = False , sweep = True , end = ( 0 + 100 j ) ) )
self . assertAlmostEqual ( ccw_half_circle . area ( ) , 3926.9908169872415 , places = 3 )
self . assertAlmostEqual ( ccw_half_circle . area ( chord_length = 1e-3 ) , 3926.9908169872415 , places = 6 )
2020-06-20 02:36:22 +00:00
def test_is_contained_by ( self ) :
enclosing_shape = Path ( )
enclosing_shape . append ( Line ( ( 0 + 0 j ) , ( 0 + 100 j ) ) )
enclosing_shape . append ( Line ( ( 0 + 100 j ) , ( 100 + 100 j ) ) )
enclosing_shape . append ( Line ( ( 100 + 100 j ) , ( 100 + 0 j ) ) )
enclosing_shape . append ( Line ( ( 100 + 0 j ) , ( 0 + 0 j ) ) )
enclosed_path = Path ( )
enclosed_path . append ( Line ( ( 10 + 10 j ) , ( 90 + 90 j ) ) )
self . assertTrue ( enclosed_path . is_contained_by ( enclosing_shape ) )
not_enclosed_path = Path ( )
not_enclosed_path . append ( Line ( ( 200 + 200 j ) , ( 200 + 0 j ) ) )
self . assertFalse ( not_enclosed_path . is_contained_by ( enclosing_shape ) )
intersecting_path = Path ( )
intersecting_path . append ( Line ( ( 50 + 50 j ) , ( 200 + 50 j ) ) )
self . assertFalse ( intersecting_path . is_contained_by ( enclosing_shape ) )
larger_shape = Path ( )
larger_shape . append ( Line ( ( - 10 - 10 j ) , ( - 10 + 110 j ) ) )
larger_shape . append ( Line ( ( - 10 + 110 j ) , ( 110 + 110 j ) ) )
larger_shape . append ( Line ( ( 110 + 110 j ) , ( 110 + - 10 j ) ) )
larger_shape . append ( Line ( ( 110 - 10 j ) , ( - 10 - 10 j ) ) )
self . assertFalse ( larger_shape . is_contained_by ( enclosing_shape ) )
self . assertTrue ( enclosing_shape . is_contained_by ( larger_shape ) )
2018-11-05 05:40:56 +00:00
2021-01-17 04:08:58 +00:00
class TestPathBugs ( unittest . TestCase ) :
def test_issue_113 ( self ) :
"""
Tests against issue regebro / svg . path #61 mathandy/svgpathtools#113
"""
p = Path ( ' M 206.5,525 Q 162.5,583 162.5,583 ' )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( p . length ( ) , 72.80109889280519 , delta = TOL )
2021-01-17 04:08:58 +00:00
p = Path ( ' M 425.781 446.289 Q 410.40000000000003 373.047 410.4 373.047 ' )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( p . length ( ) , 74.83959997888816 , delta = TOL )
2021-01-17 04:08:58 +00:00
p = Path ( ' M 639.648 568.115 Q 606.6890000000001 507.568 606.689 507.568 ' )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( p . length ( ) , 68.93645544992873 , delta = TOL )
2021-01-17 04:08:58 +00:00
p = Path ( ' M 288.818 616.699 Q 301.025 547.3629999999999 301.025 547.363 ' )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( p . length ( ) , 70.40235610403947 , delta = TOL )
2021-01-17 04:08:58 +00:00
p = Path ( ' M 339.927 706.25 Q 243.92700000000002 806.25 243.927 806.25 ' )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( p . length ( ) , 138.6217876093077 , delta = TOL )
2021-01-17 04:08:58 +00:00
p = Path ( ' M 539.795 702.637 Q 548.0959999999999 803.4669999999999 548.096 803.467 ' )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( p . length ( ) , 101.17111989594662 , delta = TOL )
2021-01-17 04:08:58 +00:00
p = Path ( ' M 537.815 555.042 Q 570.1680000000001 499.1600000000001 570.168 499.16 ' )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( p . length ( ) , 64.57177814649368 , delta = TOL )
2021-01-17 04:08:58 +00:00
p = Path ( ' M 615.297 470.503 Q 538.797 694.5029999999999 538.797 694.503 ' )
2021-06-28 05:30:56 +00:00
self . assertAlmostEqual ( p . length ( ) , 236.70287281737836 , delta = TOL )
2021-01-17 04:08:58 +00:00
def test_issue_71 ( self ) :
p = Path ( " M327 468z " )
m = p . closed
q = p . d ( ) # Failing to Crash is good.
def test_issue_95 ( self ) :
"""
Corrects :
https : / / github . com / mathandy / svgpathtools / issues / 95
"""
p = Path ( ' M261 166 L261 166 ' )
self . assertEqual ( p . length ( ) , 0 )
def test_issue_94 ( self ) :
# clipping rectangle
p1 = Path ( ' M0.0 0.0 L27.84765625 0.0 L27.84765625 242.6669922 L0.0 242.6669922 z ' )
# clipping rectangle
p2 = Path ( ' M166.8359375,235.5478516c0,3.7773438-3.0859375,6.8691406-6.8701172,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805c3.7841797,0,6.8701172,3.0927734,6.8701172,6.8701172v228.4277344z ' )
self . assertEqual ( len ( p1 . intersect ( p2 ) ) , len ( p2 . intersect ( p1 ) ) )
2016-07-06 04:51:11 +00:00
if __name__ == ' __main__ ' :
2017-03-01 07:04:37 +00:00
unittest . main ( )