Plaster/plot_utils.py

227 lines
6.3 KiB
Python
Raw Normal View History

2019-03-01 23:14:59 +01:00
# plot_utils.py
# Common geometric plotting utilities for EiBotBoard
# https://github.com/evil-mad/plotink
#
# Intended to provide some common interfaces that can be used by
# EggBot, WaterColorBot, AxiDraw, and similar machines.
#
# Version 0.4, Dated February 22, 2016.
#
#
# The MIT License (MIT)
#
# Copyright (c) 2016 Evil Mad Scientist Laboratories
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from math import sqrt
import cspsubdiv
from bezmisc import *
def version():
return "0.3" # Version number for this document
def distance( x, y ):
'''
Pythagorean theorem!
'''
return sqrt( x * x + y * y )
def parseLengthWithUnits( str ):
'''
Parse an SVG value which may or may not have units attached
This version is greatly simplified in that it only allows: no units,
units of px, and units of %. Everything else, it returns None for.
There is a more general routine to consider in scour.py if more
generality is ever needed.
'''
u = 'px'
s = str.strip()
if s[-2:] == 'px':
s = s[:-2]
elif s[-2:] == 'in':
s = s[:-2]
u = 'in'
elif s[-2:] == 'mm':
s = s[:-2]
u = 'mm'
elif s[-2:] == 'cm':
s = s[:-2]
u = 'cm'
elif s[-1:] == '%':
u = '%'
s = s[:-1]
try:
v = float( s )
except:
return None, None
return v, u
def getLength( altself, name, default ):
'''
Get the <svg> attribute with name "name" and default value "default"
Parse the attribute into a value and associated units. Then, accept
no units (''), units of pixels ('px'), and units of percentage ('%').
'''
str = altself.document.getroot().get( name )
if str:
v, u = parseLengthWithUnits( str )
if not v:
# Couldn't parse the value
return None
elif ( u == '' ) or ( u == 'px' ):
return v
elif u == 'in' :
return (float( v ) * 90.0) #90 px per inch, as of Inkscape 0.91
elif u == 'mm':
return (float( v ) * 90.0 / 25.4)
elif u == 'cm':
return (float( v ) * 90.0 / 2.54)
elif u == '%':
return float( default ) * v / 100.0
else:
# Unsupported units
return None
else:
# No width specified; assume the default value
return float( default )
def getLengthInches( altself, name ):
'''
Get the <svg> attribute with name "name" and default value "default"
Parse the attribute into a value and associated units. Then, accept
units of inches ('in'), millimeters ('mm'), or centimeters ('cm')
'''
str = altself.document.getroot().get( name )
if str:
v, u = parseLengthWithUnits( str )
if not v:
# Couldn't parse the value
return None
elif u == 'in' :
return v
elif u == 'mm':
return (float( v ) / 25.4)
elif u == 'cm':
return (float( v ) / 2.54)
else:
# Unsupported units
return None
def subdivideCubicPath( sp, flat, i=1 ):
"""
Break up a bezier curve into smaller curves, each of which
is approximately a straight line within a given tolerance
(the "smoothness" defined by [flat]).
This is a modified version of cspsubdiv.cspsubdiv(). I rewrote the recursive
call because it caused recursion-depth errors on complicated line segments.
"""
while True:
while True:
if i >= len( sp ):
return
p0 = sp[i - 1][1]
p1 = sp[i - 1][2]
p2 = sp[i][0]
p3 = sp[i][1]
b = ( p0, p1, p2, p3 )
if cspsubdiv.maxdist( b ) > flat:
break
i += 1
one, two = beziersplitatt( b, 0.5 )
sp[i - 1][2] = one[1]
sp[i][0] = two[2]
p = [one[2], one[3], two[1]]
sp[i:1] = [p]
def checkLimits( value, lowerBound, upperBound ):
#Check machine size limit; truncate at edges
if (value > upperBound):
return upperBound, True
if (value < lowerBound):
return lowerBound, True
return value, False
def vFinal_Vi_A_Dx(Vinitial,Acceleration,DeltaX):
'''
Kinematic calculation: Final velocity with constant linear acceleration.
Calculate and return the (real) final velocity, given an initial velocity,
acceleration rate, and distance interval.
Uses the kinematic equation Vf^2 = 2 a D_x + Vi^2, where
Vf is the final velocity,
a is the acceleration rate,
D_x (delta x) is the distance interval, and
Vi is the initial velocity.
We are looking at the positive root only-- if the argument of the sqrt
is less than zero, return -1, to indicate a failure.
'''
FinalVSquared = ( 2 * Acceleration * DeltaX ) + ( Vinitial * Vinitial )
if (FinalVSquared > 0):
return sqrt(FinalVSquared)
else:
return -1
def vInitial_VF_A_Dx(VFinal,Acceleration,DeltaX):
'''
Kinematic calculation: Maximum allowed initial velocity to arrive at distance X
with specified final velocity, and given maximum linear acceleration.
Calculate and return the (real) initial velocity, given an final velocity,
acceleration rate, and distance interval.
Uses the kinematic equation Vi^2 = Vf^2 - 2 a D_x , where
Vf is the final velocity,
a is the acceleration rate,
D_x (delta x) is the distance interval, and
Vi is the initial velocity.
We are looking at the positive root only-- if the argument of the sqrt
is less than zero, return -1, to indicate a failure.
'''
IntialVSquared = ( VFinal * VFinal ) - ( 2 * Acceleration * DeltaX )
if (IntialVSquared > 0):
return sqrt(IntialVSquared)
else:
return -1
def dotProductXY( inputVectorFirst, inputVectorSecond):
temp = inputVectorFirst[0] * inputVectorSecond[0] + inputVectorFirst[1] * inputVectorSecond[1]
if (temp > 1):
return 1
elif (temp < -1):
return -1
else:
return temp