Autosize module
This commit is contained in:
parent
b315aeaaf6
commit
ef613b8af7
205
lattice2Base/Autosize.py
Normal file
205
lattice2Base/Autosize.py
Normal file
|
@ -0,0 +1,205 @@
|
|||
#***************************************************************************
|
||||
#* *
|
||||
#* Copyright (c) 2018 - Victor Titov (DeepSOIC) *
|
||||
#* <vv.titov@gmail.com> *
|
||||
#* *
|
||||
#* This program is free software; you can redistribute it and/or modify *
|
||||
#* it under the terms of the GNU Lesser General Public License (LGPL) *
|
||||
#* as published by the Free Software Foundation; either version 2 of *
|
||||
#* the License, or (at your option) any later version. *
|
||||
#* for detail see the LICENCE text file. *
|
||||
#* *
|
||||
#* This program is distributed in the hope that it will be useful, *
|
||||
#* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
#* GNU Library General Public License for more details. *
|
||||
#* *
|
||||
#* You should have received a copy of the GNU Library General Public *
|
||||
#* License along with this program; if not, write to the Free Software *
|
||||
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
#* USA *
|
||||
#* *
|
||||
#***************************************************************************
|
||||
|
||||
__title__= "Lattice2 Autosize module"
|
||||
__author__ = "DeepSOIC"
|
||||
__url__ = ""
|
||||
__doc__ = (
|
||||
"""helper module for Lattice add-on workbench for FreeCAD. Routines used to guess sizes for primitives.
|
||||
"""
|
||||
)
|
||||
|
||||
from . import Rounder
|
||||
from . import Containers
|
||||
|
||||
import FreeCAD as App
|
||||
import math
|
||||
from math import radians
|
||||
|
||||
|
||||
def convenientModelWidth():
|
||||
"""convenientModelWidth(): returns a size that will conveniently fit in the width of screen"""
|
||||
return Autosize().convenientModelWidth()
|
||||
def convenientModelSize():
|
||||
"""convenientModelSize(): returns a size of a box that will conveniently fit in the screen"""
|
||||
return Autosize().convenientModelSize()
|
||||
def minimalSize():
|
||||
"""minimalSize(): returns a size that will be barely recognizable on the screen (it is a rounded pick radius in model space)"""
|
||||
return Autosize().minimalSize()
|
||||
def convenientMarkerSize():
|
||||
"""convenientMarkerSize(): size of object to be able to comfortably select faces"""
|
||||
return Autosize().convenientMarkerSize()
|
||||
def convenientFeatureSize():
|
||||
"""convenientFeatureSize(): size in between marker size and model size. Should be reasonable to edit, but not fill the whole screen."""
|
||||
return Autosize().convenientFeatureSize()
|
||||
def convenientPosition():
|
||||
return Autosize().convenientPosition()
|
||||
|
||||
def getLocalOriginPosition():
|
||||
ac = Containers.activeContainer()
|
||||
if ac is None:
|
||||
return App.Vector()
|
||||
elif ac.isDerivedFrom('App::Document'): #special case for v0.16
|
||||
return App.Vector()
|
||||
else:
|
||||
return Containers.Container(ac).getFullTransform().Base
|
||||
|
||||
class ViewportInfo(object):
|
||||
camera_type = 'perspective' #string: perspective or orthographic
|
||||
camera_placement = App.Placement(App.Vector(0,0,1), App.Rotation())
|
||||
camera_focalplacement = App.Placement()
|
||||
camera_focaldist = 1.0
|
||||
camera_heightangle = radians(60) #total horizontal view angle, in radians (for perspective camera)
|
||||
camera_height = 1 #screen height in model space (mm), for orthographic camera
|
||||
viewport_size_px = (1800,1000) #width, height of viewport, in pixels
|
||||
viewport_size_mm = (1.8,1.0) #width, height of viewport (on focal plane), in mm
|
||||
mm_per_px = 1.0 / 1000.0 #rough mm-to-pixel ratio (accurate on focal plane)
|
||||
false_viewport = False # if true, no actual viewport was queried (e.g. non-gui mode, or activeview is non-3d)
|
||||
|
||||
pickradius_px = 5
|
||||
pickradius_mm = 0.1
|
||||
|
||||
def __init__(self, viewer = None):
|
||||
try:
|
||||
if not(App.GuiUp or viewer is not None):
|
||||
return
|
||||
|
||||
if viewer == None:
|
||||
import FreeCADGui as Gui
|
||||
viewer = Gui.ActiveDocument.ActiveView
|
||||
|
||||
if not hasattr(viewer, 'getCameraNode'):
|
||||
return
|
||||
|
||||
import pivy
|
||||
cam = viewer.getCameraNode()
|
||||
self.camera_type = 'perspective' if isinstance(cam, pivy.coin.SoPerspectiveCamera) else 'orthographic'
|
||||
self.camera_placement = App.Placement(
|
||||
App.Vector(cam.position.getValue().getValue()),
|
||||
App.Rotation(*cam.orientation.getValue().getValue())
|
||||
)
|
||||
self.camera_focaldist = cam.focalDistance.getValue()
|
||||
self.camera_focalplacement = self.camera_placement.multiply(App.Placement(App.Vector(0,0,-self.camera_focaldist), App.Rotation()))
|
||||
|
||||
if self.camera_type == 'perspective':
|
||||
self.camera_heightangle = cam.heightAngle.getValue()
|
||||
self.camera_height = math.tan(self.camera_heightangle / 2) * self.camera_focaldist * 2
|
||||
else:
|
||||
self.camera_height = cam.height.getValue()
|
||||
|
||||
self.false_viewport = False
|
||||
|
||||
rman = viewer.getViewer().getSoRenderManager()
|
||||
self.viewport_size_px = tuple(rman.getWindowSize())
|
||||
|
||||
mmppx = self.camera_height/self.viewport_size_px[1]
|
||||
self.mm_per_px = mmppx
|
||||
|
||||
self.viewport_size_mm = (self.viewport_size_px[0]*mmppx, self.viewport_size_px[1]*mmppx)
|
||||
|
||||
self.pickradius_px = App.ParamGet("User parameter:BaseApp/Preferences/View").GetFloat("PickRadius", 5.0)
|
||||
self.pickradius_mm = self.pickradius_px * mmppx
|
||||
|
||||
except Exception as err:
|
||||
import traceback
|
||||
tb = traceback.format_exc()
|
||||
App.Console.PrintError("Lattice Autosize: failed to query viewport: {err}\n{tb}\n\n".format(err= str(err), tb= tb))
|
||||
|
||||
class Autosize(ViewportInfo):
|
||||
convenient_model_size_multiplier = 0.4
|
||||
def __init__(self, viewer = None):
|
||||
super(Autosize, self).__init__(viewer)
|
||||
|
||||
def convenientModelWidth(self):
|
||||
"""convenientModelWidth(): returns a size that will conveniently fit in the width of screen"""
|
||||
return Rounder.roundToNiceValue(self._convenientModelWidth())
|
||||
def convenientModelSize(self):
|
||||
"""convenientModelSize(): returns a size of a box that will conveniently fit in the screen"""
|
||||
return Rounder.roundToNiceValue(self._convenientModelSize())
|
||||
def minimalSize(self):
|
||||
"""minimalSize(): returns a size that will be barely recognizable on the screen (it is a rounded pick radius in model space)"""
|
||||
return Rounder.roundToNiceValue(self._minimalSize())
|
||||
def convenientMarkerSize(self):
|
||||
"""convenientMarkerSize(): size of object to be able to comfortably select faces"""
|
||||
return Rounder.roundToNiceValue(self._convenientMarkerSize())
|
||||
def convenientFeatureSize(self):
|
||||
"""convenientFeatureSize(): size in between marker size and model size. Should be reasonable to edit, but not fill the whole screen."""
|
||||
return Rounder.roundToNiceValue(self._convenientFeatureSize())
|
||||
|
||||
def convenientPosition(self):
|
||||
if self.isPointInWorkingArea(getLocalOriginPosition()):
|
||||
return App.Vector()
|
||||
else:
|
||||
roundfocal = Rounder.roundToNiceValue(self.camera_focaldist*0.5)
|
||||
result = App.Vector(
|
||||
[Rounder.roundToPrecision(coord, roundfocal) for coord in tuple(self.camera_focalplacement.Base)]
|
||||
)
|
||||
print result
|
||||
return result
|
||||
|
||||
def _convenientModelWidth(self):
|
||||
if self.false_viewport:
|
||||
return 10.0
|
||||
else:
|
||||
return self.viewport_size_mm[0] * self.convenient_model_size_multiplier
|
||||
def _convenientModelSize(self):
|
||||
"""_convenientMarkerSize(): (unrounded) returns size of an object that would fill most of the working area"""
|
||||
if self.false_viewport:
|
||||
return 10.0
|
||||
else:
|
||||
return min(self.viewport_size_mm[0], self.viewport_size_mm[1]) * self.convenient_model_size_multiplier
|
||||
|
||||
def _minimalSize(self):
|
||||
"""_minimalSize(): (unrounded) returns minimum object size that can be seen on screen on focal plane"""
|
||||
if self.false_viewport:
|
||||
return 0.1
|
||||
else:
|
||||
return self.pickradius_mm
|
||||
|
||||
def _convenientMarkerSize(self):
|
||||
"""_convenientMarkerSize(): (unrounded) returns maker size that is usefully large to select faces and understand its rotation"""
|
||||
if self is None:
|
||||
self = ViewportInfo()
|
||||
if self.false_viewport:
|
||||
return 1.0
|
||||
else:
|
||||
return self.pickradius_mm * 10
|
||||
|
||||
def _convenientFeatureSize(self):
|
||||
"""_convenientMarkerSize(): (unrounded) returns size of an object that would fill most of the working area"""
|
||||
if self is None:
|
||||
self = ViewportInfo()
|
||||
return math.sqrt(self._convenientModelSize() * self._convenientMarkerSize())
|
||||
|
||||
def isPointInWorkingArea(self, point = App.Vector()):
|
||||
"""isPointInWorkingArea(): returns True if point is not far from the visible area of focal plane. Point should be given in document coordinate system."""
|
||||
p_foc = self.camera_focalplacement.inverse().multVec(point)
|
||||
#p_foc is point in focal-plane CS. X and Y are along focal plane. Z is against view direction (positive = towards the camera).
|
||||
msize = self._convenientModelSize()
|
||||
mwidth = self._convenientModelWidth()
|
||||
f = self.camera_focaldist
|
||||
if abs(p_foc.x) > mwidth*0.5 or abs(p_foc.y) > msize*0.5 or p_foc.z > f*0.5 or p_foc.z < -2*f:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
278
lattice2Base/Containers.py
Normal file
278
lattice2Base/Containers.py
Normal file
|
@ -0,0 +1,278 @@
|
|||
#/***************************************************************************
|
||||
# * Copyright (c) Victor Titov (DeepSOIC) *
|
||||
# * (vv.titov@gmail.com) 2018 *
|
||||
# * *
|
||||
# * This file is part of the FreeCAD CAx development system. *
|
||||
# * *
|
||||
# * This library is free software; you can redistribute it and/or *
|
||||
# * modify it under the terms of the GNU Library General Public *
|
||||
# * License as published by the Free Software Foundation; either *
|
||||
# * version 2 of the License, or (at your option) any later version. *
|
||||
# * *
|
||||
# * This library is distributed in the hope that it will be useful, *
|
||||
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
# * GNU Library General Public License for more details. *
|
||||
# * *
|
||||
# * You should have received a copy of the GNU Library General Public *
|
||||
# * License along with this library; see the file COPYING.LIB. If not, *
|
||||
# * write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
# * Suite 330, Boston, MA 02111-1307, USA *
|
||||
# * *
|
||||
# ***************************************************************************/
|
||||
|
||||
#This is a temporary replacement for C++-powered Container class that should be eventually introduced into FreeCAD
|
||||
|
||||
import FreeCAD as App
|
||||
|
||||
class Container(object):
|
||||
"""Container class: a unified interface for container objects, such as Group, Part, Body, or Document.
|
||||
This is a temporary implementation."""
|
||||
Object = None #DocumentObject or Document, the actual container
|
||||
|
||||
def __init__(self, obj):
|
||||
self.Object = obj
|
||||
|
||||
def self_check(self):
|
||||
if self.Object is None:
|
||||
raise ValueError("Null!")
|
||||
if not isAContainer(self.Object):
|
||||
raise NotAContainerError(self.Object)
|
||||
|
||||
def getAllChildren(self):
|
||||
"""Returns all objects directly contained by the container. all = static + dynamic."""
|
||||
return self.getStaticChildren() + self.getDynamicChildren()
|
||||
|
||||
def getStaticChildren(self):
|
||||
"""Returns children tightly bound to the container, such as Origin. The key thing
|
||||
about them is that they are not supposed to be removed or added from/to the container."""
|
||||
|
||||
self.self_check()
|
||||
container = self.Object
|
||||
if container.isDerivedFrom('App::Document'):
|
||||
return []
|
||||
elif container.hasExtension('App::OriginGroupExtension'):
|
||||
if container.Origin is not None:
|
||||
return [container.Origin]
|
||||
else:
|
||||
return []
|
||||
elif container.isDerivedFrom('App::Origin'):
|
||||
return container.OriginFeatures
|
||||
elif container.hasExtension('App::GroupExtension'):
|
||||
return []
|
||||
raise RuntimeError("getStaticChildren: unexpected container type!")
|
||||
|
||||
def getDynamicChildren(self):
|
||||
"""Returns dynamic children, i.e. the stuff that can be removed from the container."""
|
||||
self.self_check()
|
||||
container = self.Object
|
||||
|
||||
if container.isDerivedFrom('App::Document'):
|
||||
# find all objects not contained by any Part or Body
|
||||
result = set(container.Objects)
|
||||
for obj in container.Objects:
|
||||
if isAContainer(obj):
|
||||
children = set(Container(obj).getAllChildren())
|
||||
result = result - children
|
||||
return list(result)
|
||||
elif container.hasExtension('App::GroupExtension'):
|
||||
result = container.Group
|
||||
if container.hasExtension('App::GeoFeatureGroupExtension'):
|
||||
#geofeaturegroup's group contains all objects within the CS, we don't want that
|
||||
result = [obj for obj in result if obj.getParentGroup() is not container]
|
||||
return result
|
||||
elif container.isDerivedFrom('App::Origin'):
|
||||
return []
|
||||
raise RuntimeError("getDynamicChildren: unexpected container type!")
|
||||
|
||||
def isACS(self):
|
||||
"""isACS(): returns true if the container forms internal coordinate system."""
|
||||
self.self_check()
|
||||
container = self.Object
|
||||
|
||||
if container.isDerivedFrom('App::Document'):
|
||||
return True #Document is a special thing... is it a CS or not is a matter of coding convenience.
|
||||
elif container.hasExtension('App::GeoFeatureGroupExtension'):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def isAVisGroup(self):
|
||||
"""isAVisGroup(): returns True if the container consumes viewproviders of children, and thus affects their visibility."""
|
||||
self.self_check()
|
||||
container = self.Object
|
||||
|
||||
if container.isDerivedFrom('App::Document'):
|
||||
return True #Document is a special thing... Return value is a matter of coding convenience.
|
||||
elif container.hasExtension('App::GeoFeatureGroupExtension'):
|
||||
return True
|
||||
elif container.isDerivedFrom('App::Origin'):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def getCSChildren(self):
|
||||
if not self.isACS():
|
||||
raise TypeError("Container is not a coordinate system")
|
||||
container = self.Object
|
||||
return _getMetacontainerChildren(self, Container.isACS)
|
||||
|
||||
def getVisGroupChildren(self):
|
||||
if not self.isAVisGroup():
|
||||
raise TypeError("Container is not a visibility group")
|
||||
container = self.Object
|
||||
return _getMetacontainerChildren(self, Container.isAVisGroup)
|
||||
|
||||
def hasObject(self, obj):
|
||||
"""Returns True if the container contains specified object directly."""
|
||||
return obj in self.getAllChildren()
|
||||
|
||||
def hasObjectRecursive(self, obj):
|
||||
return self.Object in ContainerChain(obj)
|
||||
|
||||
def Placement(self):
|
||||
if self.isACS():
|
||||
if hasattr(self.Object, 'Placement'):
|
||||
return self.Object.Placement
|
||||
return App.Placement()
|
||||
|
||||
def getFullTransform(self):
|
||||
"""getFullTransform(): returns Placement that converts coordinates of objects in this container to global CS."""
|
||||
chain = ContainerChain(self.Object) + [self.Object]
|
||||
plm = App.Placement()
|
||||
for cnt in chain:
|
||||
plm = plm.multiply(Container(cnt).Placement())
|
||||
return plm
|
||||
|
||||
def _getMetacontainerChildren(container, isrightcontainer_func):
|
||||
"""Gathers up children of metacontainer - a container structure formed by containers of specific type.
|
||||
For example, coordinate systems form a kind of container structure.
|
||||
|
||||
container: instance of Container class
|
||||
isrightcontainer_func: a function f(cnt)->bool, where cnt is a Container object."""
|
||||
|
||||
result = []
|
||||
list_traversing_now = [container] #list of Container instances
|
||||
list_to_be_traversed_next = [] #list of Container instances
|
||||
visited_containers = set([container.Object]) #set of DocumentObjects
|
||||
|
||||
while len(list_traversing_now) > 0:
|
||||
list_to_be_traversed_next = []
|
||||
for itcnt in list_traversing_now:
|
||||
children = itcnt.getAllChildren()
|
||||
result.extend(children)
|
||||
for child in children:
|
||||
if isAContainer(child):
|
||||
newcnt = Container(child)
|
||||
if not isrightcontainer_func(newcnt):
|
||||
list_to_be_traversed_next.append(newcnt)
|
||||
list_traversing_now = list_to_be_traversed_next
|
||||
|
||||
return result
|
||||
|
||||
|
||||
|
||||
def isAContainer(obj):
|
||||
'''isAContainer(obj): returns True if obj is an object container, such as
|
||||
Group, Part, Body. The important characterisic of an object being a
|
||||
container is that it can be activated to receive new objects. Documents
|
||||
are considered containers, too.'''
|
||||
|
||||
if obj.isDerivedFrom('App::Document'):
|
||||
return True
|
||||
if obj.hasExtension('App::GroupExtension'):
|
||||
return True
|
||||
if obj.isDerivedFrom('App::Origin'):
|
||||
return True
|
||||
return False
|
||||
|
||||
#from Part-o-magic...
|
||||
def ContainerOf(obj):
|
||||
"""ContainerOf(obj): returns the container that immediately has obj."""
|
||||
cnt = None
|
||||
for dep in obj.InList:
|
||||
if isAContainer(dep):
|
||||
if Container(dep).hasObject(obj):
|
||||
if cnt is not None and dep is not cnt:
|
||||
raise ContainerTreeError("Container tree is not a tree")
|
||||
cnt = dep
|
||||
if cnt is None:
|
||||
return obj.Document
|
||||
return cnt
|
||||
|
||||
def getVisGroupOf(obj):
|
||||
chain = VisGroupChain(obj)
|
||||
return chain[-1]
|
||||
|
||||
#from Part-o-magic... over-engineered, but proven to work
|
||||
def ContainerChain(feat):
|
||||
'''ContainerChain(feat): container path to feat (not including feat itself).
|
||||
Last container directly contains the feature.
|
||||
Example of output: [<document>,<SuperPart>,<Part>,<Body>]'''
|
||||
|
||||
if feat.isDerivedFrom('App::Document'):
|
||||
return []
|
||||
|
||||
list_traversing_now = [feat]
|
||||
set_of_deps = set()
|
||||
list_of_deps = []
|
||||
|
||||
while len(list_traversing_now) > 0:
|
||||
list_to_be_traversed_next = []
|
||||
for feat in list_traversing_now:
|
||||
for dep in feat.InList:
|
||||
if isAContainer(dep) and Container(dep).hasObject(feat):
|
||||
if not (dep in set_of_deps):
|
||||
set_of_deps.add(dep)
|
||||
list_of_deps.append(dep)
|
||||
list_to_be_traversed_next.append(dep)
|
||||
if len(list_to_be_traversed_next) > 1:
|
||||
raise ContainerTreeError("Container tree is not a tree")
|
||||
list_traversing_now = list_to_be_traversed_next
|
||||
|
||||
return [feat.Document] + list_of_deps[::-1]
|
||||
|
||||
def CSChain(feat):
|
||||
cnt_chain = ContainerChain(feat)
|
||||
return [cnt for cnt in cnt_chain if Container(cnt).isACS()]
|
||||
|
||||
def VisGroupChain(feat):
|
||||
cnt_chain = ContainerChain(feat)
|
||||
return [cnt for cnt in cnt_chain if Container(cnt).isAVisGroup()]
|
||||
|
||||
def activeContainer():
|
||||
'''activeContainer(): returns active container.
|
||||
If there is an active body, it is returned as active container. ActivePart is ignored.
|
||||
If there is no active body, active Part is returned.
|
||||
If there is no active Part either, active Document is returned.
|
||||
If no active document, None is returned.'''
|
||||
import FreeCAD as App
|
||||
import FreeCADGui as Gui
|
||||
|
||||
if hasattr(App, "ActiveContainer"):
|
||||
return App.ActiveContainer.Object
|
||||
|
||||
if Gui.ActiveDocument is None:
|
||||
return None
|
||||
vw = Gui.ActiveDocument.ActiveView
|
||||
if vw is None:
|
||||
return None
|
||||
if not hasattr(vw, 'getActiveObject'): #v0.16
|
||||
return App.ActiveDocument
|
||||
activeBody = vw.getActiveObject("pdbody")
|
||||
activePart = vw.getActiveObject("part")
|
||||
if activeBody:
|
||||
return activeBody
|
||||
elif activePart:
|
||||
return activePart
|
||||
else:
|
||||
return App.ActiveDocument
|
||||
|
||||
|
||||
class ContainerError(RuntimeError):
|
||||
pass
|
||||
class NotAContainerError(ContainerError):
|
||||
def __init__(self):
|
||||
ContainerError.__init__(self, u"{obj} is not recognized as container".format(obj.Name))
|
||||
class ContainerTreeError(ContainerError):
|
||||
pass
|
56
lattice2Base/Rounder.py
Normal file
56
lattice2Base/Rounder.py
Normal file
|
@ -0,0 +1,56 @@
|
|||
#***************************************************************************
|
||||
#* *
|
||||
#* Copyright (c) 2018 - Victor Titov (DeepSOIC) *
|
||||
#* <vv.titov@gmail.com> *
|
||||
#* *
|
||||
#* This program is free software; you can redistribute it and/or modify *
|
||||
#* it under the terms of the GNU Lesser General Public License (LGPL) *
|
||||
#* as published by the Free Software Foundation; either version 2 of *
|
||||
#* the License, or (at your option) any later version. *
|
||||
#* for detail see the LICENCE text file. *
|
||||
#* *
|
||||
#* This program is distributed in the hope that it will be useful, *
|
||||
#* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
#* GNU Library General Public License for more details. *
|
||||
#* *
|
||||
#* You should have received a copy of the GNU Library General Public *
|
||||
#* License along with this program; if not, write to the Free Software *
|
||||
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
#* USA *
|
||||
#* *
|
||||
#***************************************************************************
|
||||
|
||||
__title__= "Lattice2 rounder module"
|
||||
__author__ = "DeepSOIC"
|
||||
__url__ = ""
|
||||
__doc__ = "helper module for Lattice add-on workbench for FreeCAD. Provides special rounding routines."
|
||||
|
||||
import math
|
||||
from math import log
|
||||
|
||||
nice_numbers = [1.0, 2.0, 5.0]
|
||||
nice_magnitudes = []
|
||||
for degree in range(-3,6):
|
||||
order = 10.0 ** degree
|
||||
nice_magnitudes.extend([order * val for val in nice_numbers])
|
||||
|
||||
def roundToNiceValue(value, nice_value_list = nice_magnitudes):
|
||||
if value == 0.0:
|
||||
return 0.0
|
||||
|
||||
bestmatch_logdist = log(1e10)
|
||||
bestmatch = None
|
||||
|
||||
for nice_val in nice_value_list:
|
||||
logdist = abs(log(abs(value)) - log(nice_val))
|
||||
if logdist < bestmatch_logdist:
|
||||
bestmatch_logdist = logdist
|
||||
bestmatch = nice_val
|
||||
|
||||
return math.copysign(bestmatch, value)
|
||||
|
||||
def roundToPrecision(value, precision):
|
||||
if precision < 1e-12:
|
||||
return value
|
||||
return round(value/precision)*precision
|
0
lattice2Base/__init__.py
Normal file
0
lattice2Base/__init__.py
Normal file
Loading…
Reference in New Issue
Block a user