<commit_before>"""
This page is in the table of contents.
Comb is a craft plugin to bend the extruder travel paths around holes in the slices, to avoid stringers.

The comb manual page is at:
http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Comb

==Operation==
The default 'Activate Comb' checkbox is off.  When it is on, the functions described below will work, when it is off, nothing will be done.

==Settings==
===Running Jump Space===
Default: 2 mm

Defines the running jump space that is added before going from one island to another.  If the running jump space is greater than zero, the departure from the island will also be brought closer to the arrival point on the next island so that the stringer between islands will be shorter.  For an extruder with acceleration code, an extra space before leaving the island means that it will be going at high speed as it exits the island, which means the stringer between islands will be thinner.

==Examples==
The following examples comb the file Screw Holder Bottom.stl.  The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and comb.py.

> python comb.py
This brings up the comb dialog.

> python comb.py Screw Holder Bottom.stl
The comb tool is parsing the file:
Screw Holder Bottom.stl
..
The comb tool has created the file:
.. Screw Holder Bottom_comb.gcode

"""

from __future__ import absolute_import
try:
	import psyco
	psyco.full()
except:
	pass
#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module.
import __init__

from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret
from fabmetheus_utilities import archive
from fabmetheus_utilities import euclidean
from fabmetheus_utilities import gcodec
from fabmetheus_utilities import intercircle
from fabmetheus_utilities import settings
from skeinforge_application.skeinforge_utilities import skeinforge_craft
from skeinforge_application.skeinforge_utilities import skeinforge_polyfile
from skeinforge_application.skeinforge_utilities import skeinforge_profile
import math
import sys


__author__ = '<NAME> (<EMAIL>)'
__date__ = '$Date: 2008/21/04 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'


def getCraftedText(fileName, text, repository=None):
	"Comb a gcode linear move text."
	return getCraftedTextFromText(archive.getTextIfEmpty(fileName, text), repository)

def getCraftedTextFromText(gcodeText, repository=None):
	"Comb a gcode linear move text."
	if gcodec.isProcedureDoneOrFileIsEmpty(gcodeText, 'comb'):
		return gcodeText
	if repository == None:
		repository = settings.getReadRepository(CombRepository())
	if not repository.activateComb.value:
		return gcodeText
	return CombSkein().getCraftedGcode(gcodeText, repository)

def getJumpPoint(begin, end, loop, runningJumpSpace):
	'Get running jump point inside loop.'
	segment = begin - end
	segmentLength = abs(segment)
	if segmentLength == 0.0:
		return begin
	segment /= segmentLength
	distancePoint = DistancePoint(begin, loop, runningJumpSpace, segment)
	if distancePoint.distance == runningJumpSpace:
		return distancePoint.point
	effectiveDistance = distancePoint.distance
	jumpPoint = distancePoint.point
	segmentLeft = complex(0.70710678118654757, -0.70710678118654757)
	distancePoint = DistancePoint(begin, loop, runningJumpSpace, segmentLeft)
	distancePoint.distance *= 0.5
	if distancePoint.distance > effectiveDistance:
		effectiveDistance = distancePoint.distance
		jumpPoint = distancePoint.point
	segmentRight = complex(0.70710678118654757, 0.70710678118654757)
	distancePoint = DistancePoint(begin, loop, runningJumpSpace, segmentRight)
	distancePoint.distance *= 0.5
	if distancePoint.distance > effectiveDistance:
		effectiveDistance = distancePoint.distance
		jumpPoint = distancePoint.point
	return jumpPoint

def getJumpPointIfInside(boundary, otherPoint, edgeWidth, runningJumpSpace):
	'Get the jump point if it is inside the boundary, otherwise return None.'
	insetBoundary = intercircle.getSimplifiedInsetFromClockwiseLoop(boundary, -edgeWidth)
	closestJumpDistanceIndex = euclidean.getClosestDistanceIndexToLine(otherPoint, insetBoundary)
	jumpIndex = (closestJumpDistanceIndex.index + 1) % len(insetBoundary)
	jumpPoint = euclidean.getClosestPointOnSegment(insetBoundary[closestJumpDistanceIndex.index], insetBoundary[jumpIndex], otherPoint)
	jumpPoint = getJumpPoint(jumpPoint, otherPoint, boundary, runningJumpSpace)
	if euclidean.isPointInsideLoop(boundary, jumpPoint):
		return jumpPoint
	return None

def getNewRepository():
	'Get new repository.'
	return CombRepository()

def getPathsByIntersectedLoop(begin, end, loop):
	'Get both paths along the loop from the point closest to the begin to the point closest to the end.'
	closestBeginDistanceIndex = euclidean.getClosestDistanceIndexToLine(begin, loop)
	closestEndDistanceIndex = euclidean.getClosestDistanceIndexToLine(end, loop)
	beginIndex = (closestBeginDistanceIndex.index + 1) % len(loop)
	endIndex = (closestEndDistanceIndex.index + 1) % len(loop)
	closestBegin = euclidean.getClosestPointOnSegment(loop[closestBeginDistanceIndex.index], loop[beginIndex], begin)
	closestEnd = euclidean.getClosestPointOnSegment(loop[closestEndDistanceIndex.index], loop[endIndex], end)
	clockwisePath = [closestBegin]
	widdershinsPath = [closestBegin]
	if closestBeginDistanceIndex.index != closestEndDistanceIndex.index:
		widdershinsPath += euclidean.getAroundLoop(beginIndex, endIndex, loop)
		clockwisePath += euclidean.getAroundLoop(endIndex, beginIndex, loop)[: : -1]
	clockwisePath.append(closestEnd)
	widdershinsPath.append(closestEnd)
	return [clockwisePath, widdershinsPath]

def writeOutput(fileName, shouldAnalyze=True):
	"Comb a gcode linear move file."
	skeinforge_craft.writeChainTextWithNounMessage(fileName, 'comb', shouldAnalyze)


class BoundarySegment:
	'A boundary and segment.'
	def __init__(self, begin):
		'Initialize'
		self.segment = [begin]

	def getSegment(self, boundarySegmentIndex, boundarySegments, edgeWidth, runningJumpSpace):
		'Get both paths along the loop from the point closest to the begin to the point closest to the end.'
		negativeEdgeWidth = -edgeWidth
		nextBoundarySegment = boundarySegments[boundarySegmentIndex + 1]
		nextBegin = nextBoundarySegment.segment[0]
		end = getJumpPointIfInside(self.boundary, nextBegin, edgeWidth, runningJumpSpace)
		if end == None:
			end = self.boundary.segment[1]
		nextBegin = getJumpPointIfInside(nextBoundarySegment.boundary, end, edgeWidth, runningJumpSpace)<commit_msg>Fixed bug in comb<commit_after>"""
This page is in the table of contents.
Comb is a craft plugin to bend the extruder travel paths around holes in the slices, to avoid stringers.

The comb manual page is at:
http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Comb

==Operation==
The default 'Activate Comb' checkbox is off.  When it is on, the functions described below will work, when it is off, nothing will be done.

==Settings==
===Running Jump Space===
Default: 2 mm

Defines the running jump space that is added before going from one island to another.  If the running jump space is greater than zero, the departure from the island will also be brought closer to the arrival point on the next island so that the stringer between islands will be shorter.  For an extruder with acceleration code, an extra space before leaving the island means that it will be going at high speed as it exits the island, which means the stringer between islands will be thinner.

==Examples==
The following examples comb the file Screw Holder Bottom.stl.  The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and comb.py.

> python comb.py
This brings up the comb dialog.

> python comb.py Screw Holder Bottom.stl
The comb tool is parsing the file:
Screw Holder Bottom.stl
..
The comb tool has created the file:
.. Screw Holder Bottom_comb.gcode

"""

from __future__ import absolute_import
try:
	import psyco
	psyco.full()
except:
	pass
#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module.
import __init__

from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret
from fabmetheus_utilities import archive
from fabmetheus_utilities import euclidean
from fabmetheus_utilities import gcodec
from fabmetheus_utilities import intercircle
from fabmetheus_utilities import settings
from skeinforge_application.skeinforge_utilities import skeinforge_craft
from skeinforge_application.skeinforge_utilities import skeinforge_polyfile
from skeinforge_application.skeinforge_utilities import skeinforge_profile
import math
import sys


__author__ = '<NAME> (<EMAIL>)'
__date__ = '$Date: 2008/21/04 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'


def getCraftedText(fileName, text, repository=None):
	"Comb a gcode linear move text."
	return getCraftedTextFromText(archive.getTextIfEmpty(fileName, text), repository)

def getCraftedTextFromText(gcodeText, repository=None):
	"Comb a gcode linear move text."
	if gcodec.isProcedureDoneOrFileIsEmpty(gcodeText, 'comb'):
		return gcodeText
	if repository == None:
		repository = settings.getReadRepository(CombRepository())
	if not repository.activateComb.value:
		return gcodeText
	return CombSkein().getCraftedGcode(gcodeText, repository)

def getJumpPoint(begin, end, loop, runningJumpSpace):
	'Get running jump point inside loop.'
	segment = begin - end
	segmentLength = abs(segment)
	if segmentLength == 0.0:
		return begin
	segment /= segmentLength
	distancePoint = DistancePoint(begin, loop, runningJumpSpace, segment)
	if distancePoint.distance == runningJumpSpace:
		return distancePoint.point
	effectiveDistance = distancePoint.distance
	jumpPoint = distancePoint.point
	segmentLeft = complex(0.70710678118654757, -0.70710678118654757)
	distancePoint = DistancePoint(begin, loop, runningJumpSpace, segmentLeft)
	distancePoint.distance *= 0.5
	if distancePoint.distance > effectiveDistance:
		effectiveDistance = distancePoint.distance
		jumpPoint = distancePoint.point
	segmentRight = complex(0.70710678118654757, 0.70710678118654757)
	distancePoint = DistancePoint(begin, loop, runningJumpSpace, segmentRight)
	distancePoint.distance *= 0.5
	if distancePoint.distance > effectiveDistance:
		effectiveDistance = distancePoint.distance
		jumpPoint = distancePoint.point
	return jumpPoint

def getJumpPointIfInside(boundary, otherPoint, edgeWidth, runningJumpSpace):
	'Get the jump point if it is inside the boundary, otherwise return None.'
	insetBoundary = intercircle.getSimplifiedInsetFromClockwiseLoop(boundary, -edgeWidth)
	closestJumpDistanceIndex = euclidean.getClosestDistanceIndexToLine(otherPoint, insetBoundary)
	jumpIndex = (closestJumpDistanceIndex.index + 1) % len(insetBoundary)
	jumpPoint = euclidean.getClosestPointOnSegment(insetBoundary[closestJumpDistanceIndex.index], insetBoundary[jumpIndex], otherPoint)
	jumpPoint = getJumpPoint(jumpPoint, otherPoint, boundary, runningJumpSpace)
	if euclidean.isPointInsideLoop(boundary, jumpPoint):
		return jumpPoint
	return None

def getNewRepository():
	'Get new repository.'
	return CombRepository()

def getPathsByIntersectedLoop(begin, end, loop):
	'Get both paths along the loop from the point closest to the begin to the point closest to the end.'
	closestBeginDistanceIndex = euclidean.getClosestDistanceIndexToLine(begin, loop)
	closestEndDistanceIndex = euclidean.getClosestDistanceIndexToLine(end, loop)
	beginIndex = (closestBeginDistanceIndex.index + 1) % len(loop)
	endIndex = (closestEndDistanceIndex.index + 1) % len(loop)
	closestBegin = euclidean.getClosestPointOnSegment(loop[closestBeginDistanceIndex.index], loop[beginIndex], begin)
	closestEnd = euclidean.getClosestPointOnSegment(loop[closestEndDistanceIndex.index], loop[endIndex], end)
	clockwisePath = [closestBegin]
	widdershinsPath = [closestBegin]
	if closestBeginDistanceIndex.index != closestEndDistanceIndex.index:
		widdershinsPath += euclidean.getAroundLoop(beginIndex, endIndex, loop)
		clockwisePath += euclidean.getAroundLoop(endIndex, beginIndex, loop)[: : -1]
	clockwisePath.append(closestEnd)
	widdershinsPath.append(closestEnd)
	return [clockwisePath, widdershinsPath]

def writeOutput(fileName, shouldAnalyze=True):
	"Comb a gcode linear move file."
	skeinforge_craft.writeChainTextWithNounMessage(fileName, 'comb', shouldAnalyze)


class BoundarySegment:
	'A boundary and segment.'
	def __init__(self, begin):
		'Initialize'
		self.segment = [begin]

	def getSegment(self, boundarySegmentIndex, boundarySegments, edgeWidth, runningJumpSpace):
		'Get both paths along the loop from the point closest to the begin to the point closest to the end.'
		negativeEdgeWidth = -edgeWidth
		nextBoundarySegment = boundarySegments[boundarySegmentIndex + 1]
		nextBegin = nextBoundarySegment.segment[0]
		end = getJumpPointIfInside(self.boundary, nextBegin, edgeWidth, runningJumpSpace)
		if end == None:
			end = self.segment[1]
		nextBegin = getJumpPointIfInside(nextBoundarySegment.boundary, end, edgeWidth, runningJumpSpace)
{'fraction_non_alphanumeric': 0.07091500794671914, 'fraction_numerical': 0.014228411413002346, 'mean_word_length': 6.201089918256131, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5043411', 'n_tokens_mistral': 4141, 'n_tokens_neox': 3810, 'n_words': 1228}