/* */ //port to python //getNearestMissOfOddPerimeter //maybe rename elementAt, get //in getPlanePointIndex should find point on line instead of pulling line? //make movement paths after fill connections and reversals //choice span truncations //add warning if requested layer is outside shape //one file for all, file for each object //multilayer perimeter //later //start close from end of lower layer //speed up, port dicer to python //go to unproven if open loop //clean cycle between layers //delay in extruding? // subtract paths from each other //balconies //input perimeters? //probably never below //arc output should probably be in next program //connect perimeters //output slice & dice xml? if requested //choice draw single line extensions already partly done //choice fillet & slow in corners, probably not necessary because arcs are better //loop infill? is probably not necessary //might but probably is not necessary to watch out for intersections when adding closest //translate to xml //dimension polygons //parse and serial output //show direction of face void addAroundClosest( Vector paths, removedEndpointPoint ) { closestDistanceSquared = 999999999.0; closestPath = null; closestPointIndex = null; for ( int pathIndex = 0; pathIndex < paths.size(); pathIndex++ ) { path = paths.get( pathIndex ); for ( int pointIndex = 0; pointIndex < path.size(); pointIndex++ ) { point = path.get( pointIndex ); distanceSquared = point.distance2( removedEndpointPoint ); if ( distanceSquared < closestDistanceSquared ) { closestDistanceSquared = distanceSquared; closestPath = path; closestPointIndex = pointIndex; } } } if ( closestPath == null ) { return; } if ( closestPointIndex + 1 == closestPath.size() ) { closestPath.insertElementAt( removedEndpointPoint, closestPointIndex + 1 ); return; } if ( closestPointIndex == 0 ) { closestPath.insertElementAt( removedEndpointPoint, 0 ); return; } beforePoint = closestPath.get( closestPointIndex - 1 ); centerPoint = closestPath.get( closestPointIndex ); afterPoint = closestPath.get( closestPointIndex + 1 ); beforeSegment = beforePoint.minus( removedEndpointPoint ); beforeSegment.normalize(); centerSegment = centerPoint.minus( removedEndpointPoint ); centerSegment.normalize(); afterSegment = afterPoint.minus( removedEndpointPoint ); afterSegment.normalize(); beforePlaneDot = getPlaneDotPlusOne( beforeSegment, centerSegment ); afterPlaneDot = getPlaneDotPlusOne( centerSegment, afterSegment ); if ( beforePlaneDot < afterPlaneDot ) { if ( beforePlaneDot < 2.0 ) { closestPath.insertElementAt( removedEndpointPoint, closestPointIndex ); } return; } if ( afterPlaneDot < 2.0 ) { closestPath.insertElementAt( removedEndpointPoint, closestPointIndex + 1 ); } } void addCircleIntersectionLoop( Vector circleIntersectionPath, Vector circleIntersections ) { firstCircleIntersection = circleIntersectionPath.get( 0 ); circleIntersectionAhead = firstCircleIntersection; for ( int circleIntersectionIndex = 0; circleIntersectionIndex < circleIntersections.size(); circleIntersectionIndex++ ) { circleIntersectionAhead = circleIntersectionAhead.getCircleIntersectionAhead(); if ( circleIntersectionAhead.index == firstCircleIntersection.index ) { return; } circleIntersectionAhead.addToVector( circleIntersectionPath ); } print( "getCircleIntersectionPath would have gone into an endless loop, this should never happen." ); print( circleIntersectionPath ); print( "firstCircleIntersection" ); print( firstCircleIntersection.toString() ); print( circleIntersections ); for ( int circleIntersectionIndex = 0; circleIntersectionIndex < circleIntersections.size(); circleIntersectionIndex++ ) { print( circleIntersections.get( circleIntersectionIndex ).toString() ); } } void addCircleNodesToScene( Vector circleNodes ) { if ( circleNodes.size() < 1 ) { return; } Vector path = new Vector(); double radius = circleNodes.get( 0 ).radius; for ( int circleNodeIndex = 0; circleNodeIndex < circleNodes.size(); circleNodeIndex++ ) { circumference = getCircumference( circleNodes.get( circleNodeIndex ).circle, radius ); path.addAll( Arrays.asList( circumference ) ); } displayPath( "Intersecting Circles", path.toArray( new Vec3[ path.size() ] ) ); } void addEdgePair( Hashtable edgePairTable, TriangleMesh.Edge[] edges, int faceEdgeIndex, int remainingEdgeIndex, Hashtable remainingEdgeTable ) { if ( faceEdgeIndex == remainingEdgeIndex ) { return; } if ( !remainingEdgeTable.containsKey( faceEdgeIndex ) ) { return; } edgePair = EdgePair().setIndexFirstSecond( remainingEdgeIndex, faceEdgeIndex, edges ); edgePairTable.put( edgePair.toString(), edgePair ); } void addFromAway( Vec3[] loop, int loopExitIndex, Vec3 loopExitPoint, Vector path ) { segmentBegin = loop[ loopExitIndex ]; endIndex = ( loopExitIndex + 1 ) % loop.length; segmentEnd = loop[ endIndex ]; startLength = halfExtrusionWidth + absoluteFillPerimeterOverlap; nearestPoint = getNearestPointOnSegment( segmentBegin, segmentEnd, loopExitPoint ); planeExtruderApart = getPlanePointIndex( startLength, nearestPoint, endIndex, loop ); path.add( new Vec3( planeExtruderApart.x, planeExtruderApart.y, nearestPoint.z ) ); path.addAll( getAroundLoop( ( int )planeExtruderApart.z, loopExitIndex + 1, loop ) ); path.add( nearestPoint ); } void addPath( Vector fill, Vector path, int layer, Vec2 move, Vec2 rotationPlaneAngle ) { pathArray = path.toArray( new Vec3[ path.size() ] ); pathArray = getSimpifiedVertexPositions( pathArray, extrusionWidth ); planeRotated = getPlaneArrayRotatedByVector( rotationPlaneAngle, pathArray ); fill.add( planeRotated ); } void addPointsFromSegment( Vector points, double radius, Vec3 vertexPositionBegin, Vec3 vertexPositionEnd ) { double thresholdRadius = radius * 0.6;// a higher number would be faster but would leave bigger dangling loops double thresholdDiameter = thresholdRadius * 2.0; segment = vertexPositionEnd.minus( vertexPositionBegin ); segmentLength = segment.length(); int extraCircles = ( int )Math.floor( segmentLength / ( double )thresholdDiameter ); double lengthIncrement = segmentLength / ( ( double )extraCircles + 1.0 ); segment.scale( lengthIncrement / segmentLength ); nextCircleCenter = vertexPositionBegin.plus( segment ); for ( int circleIndex = 0; circleIndex < extraCircles; circleIndex++ ) { points.add( nextCircleCenter ); nextCircleCenter = nextCircleCenter.plus( segment ); } } void addSparseEndpoints( Vector endpoints, int fillLine, Object[] horizontalSegments, Vector removedEndpoints, Vector surroundingXIntersections ) { Vector horizontalEndpoints = horizontalSegments[ fillLine ]; if ( horizontalEndpoints == null ) { return; } for ( int endpointIndex = 0; endpointIndex < horizontalEndpoints.size(); endpointIndex++ ) { segment = horizontalEndpoints.get( endpointIndex ); addSparseEndpointsFromSegment( endpoints, fillLine, horizontalSegments, removedEndpoints, segment, surroundingXIntersections ); } } void addSparseEndpointsFromSegment( Vector endpoints, int fillLine, Object[] horizontalSegments, Vector removedEndpoints, Object[] segment, Vector surroundingXIntersections ) { endpointFirstPoint = segment[ 0 ].point; endpointSecondPoint = segment[ 1 ].point; boolean shouldFill = fillLine < 1 || fillLine >= horizontalSegments.length - 1 || surroundingXIntersections == null; if ( fillDensity > 0.0 ) { if ( ( int )Math.round( Math.round( fillLine * fillDensity ) / fillDensity ) == fillLine ) { shouldFill = true; } } if ( endpointFirstPoint.distance( endpointSecondPoint ) < 3.0 * extrusionWidth ) { shouldFill = true; } if ( shouldFill ) { endpoints.addAll( Arrays.asList( segment ) ); return; } if ( !isSegmentAround( horizontalSegments[ fillLine - 1 ], segment ) ) { endpoints.addAll( Arrays.asList( segment ) ); return; } if ( !isSegmentAround( horizontalSegments[ fillLine + 1 ], segment ) ) { endpoints.addAll( Arrays.asList( segment ) ); return; } for ( int surroundingIndex = 0; surroundingIndex < surroundingXIntersections.size(); surroundingIndex += 2 ) { surroundingXFirst = surroundingXIntersections.get( surroundingIndex ); surroundingXSecond = surroundingXIntersections.get( surroundingIndex + 1 ); if ( isSegmentCompletelyInX( segment, surroundingXFirst, surroundingXSecond ) ) { removedEndpoints.addAll( Arrays.asList( segment ) ); return; } } endpoints.addAll( Arrays.asList( segment ) ); } /** Add a delete the last object of the scene, undo record, to the scene. */ void addUndoRecord() { scene = window.getScene(); undoAdd = new UndoRecord ( window, false, UndoRecord.DELETE_OBJECT, new Object[] { new Integer( scene.getNumObjects() - 1 ) } ); window.setUndoRecord( undoAdd ); } void addXIntersections( Vec3[] loop, int solidIndex, Vector xIntersectionVector, double y ) { for ( int pointIndex = 0; pointIndex < loop.length; pointIndex++ ) { pointFirst = loop[ pointIndex ]; pointSecond = loop[ ( pointIndex + 1 ) % loop.length ]; isYAboveFirst = y > pointFirst.y; isYAboveSecond = y > pointSecond.y; if ( isYAboveFirst != isYAboveSecond ) { xIntersection = getXIntersection( pointFirst, pointSecond, y ); xIntersectionVector.add( new Vec2( xIntersection, ( double )solidIndex ) ); } } } void addXIntersectionsFromLoops( Vector loops, int solidIndex, Vector xIntersectionVector, double y ) { for ( int loopIndex = 0; loopIndex < loops.size(); loopIndex++ ) { loop = loops.get( loopIndex ); addXIntersections( loop, solidIndex, xIntersectionVector, y ); } } CircleIntersection() { circleNodeAhead = null; circleNodeBehind = null; boolean steppedOn = false; int index = 0; void addToVector( Vector circleIntersectionPath ) { steppedOn = true; circleIntersectionPath.add( this ); } Vec3 getAbsolutePosition() { return getPositionRelativeToBehind().plus( circleNodeBehind.circle ); } getCircleIntersectionAhead() { circleIntersections = circleNodeAhead.circleIntersections; circleIntersectionAhead = null; smallestWiddershinsDot = 999999999.0; Vec3 positionRelativeToAhead = getAbsolutePosition().minus( circleNodeAhead.circle ); positionRelativeToAhead.normalize(); for ( int circleIntersectionIndex = 0; circleIntersectionIndex < circleIntersections.size(); circleIntersectionIndex++ ) { circleIntersection = circleIntersections.get( circleIntersectionIndex ); circleIntersectionRelative = circleIntersection.getPositionRelativeToBehind(); circleIntersectionRelative.normalize(); widdershinsDot = getWiddershinsDot( positionRelativeToAhead, circleIntersectionRelative ); if ( widdershinsDot < smallestWiddershinsDot ) { smallestWiddershinsDot = widdershinsDot; circleIntersectionAhead = circleIntersection; } } return circleIntersectionAhead; } Vec3 getPositionRelativeToBehind() { Vec3 aheadMinusBehind = circleNodeAhead.circle.minus( circleNodeBehind.circle ); aheadMinusBehind.scale( 0.5 ); halfChordWidth = Math.sqrt( circleNodeAhead.radiusSquared - aheadMinusBehind.length2() ); rotatedClockwiseQuarter = getRotatedClockwiseQuarterAroundZAxis( aheadMinusBehind ); rotatedClockwiseQuarter.scale( halfChordWidth / rotatedClockwiseQuarter.length() ); aheadMinusBehind.add( rotatedClockwiseQuarter ); return aheadMinusBehind; } boolean isWithinCircles( Vector circleNodes ) { vertexPosition = getAbsolutePosition(); for ( int circleNodeIndex = 0; circleNodeIndex < circleNodes.size(); circleNodeIndex++ ) { circleNode = circleNodes.get( circleNodeIndex ); if ( circleNode != circleNodeAhead && circleNode != circleNodeBehind ) { if ( circleNode.circle.distance2( vertexPosition ) < circleNodeAhead.radiusSquared ) { return true; } } } return false; } setCircleNodes( inputCircleNodeAhead, inputIndex, inputCircleNodeBehind ) { index = inputIndex; circleNodeAhead = inputCircleNodeAhead; circleNodeBehind = inputCircleNodeBehind; return this; } toString() { outputString = "index " + index.toString() + " " + getAbsolutePosition().toString() + " " + circleNodeBehind.index.toString() + " " + circleNodeBehind.circle.toString() + " "; return outputString + circleNodeAhead.index.toString() + " " + circleNodeAhead.circle.toString(); } return this; } CircleNode() { Vector circleIntersections = new Vector(); Vec3 circle = null; int index = 0; double diameterSquared = 0.0; double radius = 0.0; double radiusSquared = 0.0; isWithin( inputCircle ) { return circle.distance2( inputCircle ) < diameterSquared; } setCircleRadius( inputCircle, inputIndex, inputRadius ) { circle = inputCircle; index = inputIndex; radius = inputRadius; radiusSquared = radius * radius; diameterSquared = 4.0 * radiusSquared; return this; } toString() { return index.toString() + " " + circle.toString() + " "; } return this; } /** Display an open path. @param name name of path which will be added to the scene @param vertexPositions path vertex positions */ void displayPath( String name, Vec3[] vertexPositions ) { scene = window.getScene(); CoordinateSystem coordinateSystem = new CoordinateSystem(); float[] smoothness = new float[ vertexPositions.length ]; String parentName = "Skeinforge Parent"; parentCurveObjectInfo = scene.getObject( parentName ); if ( parentCurveObjectInfo == null ) { name = parentName; } if ( name == "" ) { name = "Child Polygon Slice"; } curve = new Curve( vertexPositions, smoothness, Mesh.APPROXIMATING, false ); window.addObject( curve, coordinateSystem, name, null ); addUndoRecord(); lastObjectInfo = scene.getObject( scene.getNumObjects() - 1 ); if ( parentCurveObjectInfo != null ) { parentCurveObjectInfo.addChild( lastObjectInfo, parentCurveObjectInfo.children.length ); } } EdgePair() { int edgeIndexFirst = 0; int edgeIndexSecond = 0; TriangleMesh.Edge edgeFirst = null; TriangleMesh.Edge edgeSecond = null; addPointsAtZ( Vector points, double radius, Vec3[] vertexPositions, double z ) { sliceIntersectionFirst = getSliceIntersectionFromEdge( edgeFirst, vertexPositions, z ); sliceIntersectionSecond = getSliceIntersectionFromEdge( edgeSecond, vertexPositions, z ); addPointsFromSegment( points, radius, sliceIntersectionFirst, sliceIntersectionSecond ); } setIndexFirstSecond( inputEdgeIndexFirst, inputEdgeIndexSecond, TriangleMesh.Edge[] edges ) { edgeIndexFirst = inputEdgeIndexFirst; edgeIndexSecond = inputEdgeIndexSecond; if ( inputEdgeIndexSecond < inputEdgeIndexFirst ) { edgeIndexFirst = inputEdgeIndexSecond; edgeIndexSecond = inputEdgeIndexFirst; } edgeFirst = edges[ edgeIndexFirst ]; edgeSecond = edges[ edgeIndexSecond ]; return this; } toString() { return edgeIndexFirst.toString() + " " + edgeIndexSecond.toString(); } return this; } Endpoint() { otherEndpoint = null; Vec3 point = null; getMiddle() { middle = point.plus( otherEndpoint.point ); middle.scale( 0.5 ); return middle; } getNearestEndpoint( Vector endpoints ) { smallestDistanceSquared = 999999999999999999.0; nearestEndpoint = null; for ( int endpointIndex = 0; endpointIndex < endpoints.size(); endpointIndex++ ) { endpoint = endpoints.get( endpointIndex ); distanceSquared = point.Distance2( endpoint.point ); if ( distanceSquared < smallestDistanceSquared ) { smallestDistanceSquared = distanceSquared; nearestEndpoint = endpoint; } } return nearestEndpoint; } getNearestMiddleIndex( Vector middles ) { smallestDistance = 999999999.0; nearestMiddleIndex = 0; for ( int middleIndex = 0; middleIndex < middles.size(); middleIndex++ ) { middle = middles.get( middleIndex ); distance = point.distance2( middle ); if ( distance < smallestDistance ) { smallestDistance = distance; nearestMiddleIndex = middleIndex; } } return nearestMiddleIndex; } getNearestMiss( Vector arounds, Vector endpoints, Vector stretchedXSegments ) { smallestDistanceSquared = 999999999999999999.0; nearestMiss = null; for ( int endpointIndex = 0; endpointIndex < endpoints.size(); endpointIndex++ ) { endpoint = endpoints.get( endpointIndex ); segment = endpoint.point.minus( point ); segmentYMirror = new Vec2( segment.x, - segment.y ); segmentYMirror.normalize(); segmentFirstPoint = getRoundZAxisByPlaneAngle( segmentYMirror, point ); segmentSecondPoint = getRoundZAxisByPlaneAngle( segmentYMirror, endpoint.point ); distanceSquared = point.distance2( endpoint.point ); if ( distanceSquared < smallestDistanceSquared ) { if ( !isLoopVectorCrossingInsideXSegment( arounds, segmentFirstPoint.x, segmentSecondPoint.x, segmentYMirror, segmentFirstPoint.y ) ) { if ( !isPointCrossingSegments( endpoint.point, stretchedXSegments ) ) { smallestDistanceSquared = distanceSquared; nearestMiss = endpoint; } } } } return nearestMiss; } boolean isLoopVectorCrossingInsideXSegment( Vector loopVector, double segmentFirstX, double segmentSecondX, Vec2 segmentYMirror, double y ) { for ( int loopIndex = 0; loopIndex < loopVector.size(); loopIndex++ ) { alreadyFilledLoop = loopVector.get( loopIndex ); vertexPositions = getPlaneArrayRotatedByVector( segmentYMirror, alreadyFilledLoop ); for ( int vertexPositionIndex = 0; vertexPositionIndex < vertexPositions.length; vertexPositionIndex++ ) { vertexPositionFirst = vertexPositions[ vertexPositionIndex ]; vertexPositionSecond = vertexPositions[ ( vertexPositionIndex + 1 ) % vertexPositions.length ]; if ( isLineCrossingInsideXSegment( segmentFirstX, segmentSecondX, vertexPositionFirst, vertexPositionSecond, y ) ) { return true; } } } return false; } boolean isPointCrossingSegments( Vec3 inputPoint, Vector stretchedXSegments ) { double endpointRadius = 0.1 * extrusionWidth; Vec3 segment = inputPoint.minus( point ); double alongSegmentLength = 0.333 * segment.length(); alongSegmentLength = Math.min( endpointRadius, alongSegmentLength ); segment.scale( alongSegmentLength / segment.length() ); attractedPoint = point.plus( segment ); attractedInputPoint = inputPoint.minus( segment ); for ( int stretchedIndex = 0; stretchedIndex < stretchedXSegments.size(); stretchedIndex++ ) { stretchedXSegment = stretchedXSegments.get( stretchedIndex ); if ( isLineCrossingInsideXSegment( stretchedXSegment.xMinimum, stretchedXSegment.xMaximum, attractedPoint, attractedInputPoint, stretchedXSegment.y ) ) { return true; } } return false; } setOtherPoint( inputOtherEndpoint, inputPoint ) { otherEndpoint = inputOtherEndpoint; point = inputPoint; return this; } toString() { return point.toString() + " " + otherEndpoint.point.toString(); } return this; } StretchedXSegment() { double xMaximum = 0.0; double xMinimum = 0.0; double y = 0.0; setXXY( double firstX, double inputY, double secondX, double stretch ) { xMaximum = Math.max( firstX, secondX ) + stretch; xMinimum = Math.min( firstX, secondX ) - stretch; y = inputY; return this; } toString() { return xMinimum.toString() + " " + xMaximum.toString() + " " + y.toString(); } return this; } Vector getAroundLoop( int begin, int end, Vec3[] loop ) { Vector aroundLoop = new Vector(); if ( end <= begin ) { end += loop.length; } for ( int vertexIndex = begin; vertexIndex < end; vertexIndex++ ) { aroundLoop.add( loop[ vertexIndex % loop.length ] ); } return aroundLoop; } /** Get symmetrical hollow arrow. @param begin beginning of arrow @param segment arrow tip minus base @return symmetrical hollow arrow */ Vec3[] getArrow( Vec3 begin, Vec3 segment ) { Vec3[] arrow = new Vec3[ 7 ]; int arrowIndex = 0; double rotation = Math.PI * 0.7; arrow[ arrowIndex++ ] = begin; rotatedWiddershinsSegment = getRotatedAroundZAxis( rotation, segment ).times( 0.4 ); arrow[ arrowIndex++ ] = rotatedWiddershinsSegment.plus( begin ); segmentPosition = segment.plus( begin ); arrow[ arrowIndex++ ] = segmentPosition; rotatedClockwiseSegment = getRotatedAroundZAxis( - rotation, segment ).times( 0.4 ); rotatedClockwisePosition = rotatedClockwiseSegment.plus( begin ); arrow[ arrowIndex++ ] = rotatedClockwisePosition; arrow[ arrowIndex++ ] = begin; arrow[ arrowIndex++ ] = rotatedClockwisePosition; arrow[ arrowIndex++ ] = segmentPosition; return arrow; } Vec3[] getAwayVertexPositions( Vec3[] vertexPositions, double radius ) { Vector away = new Vector(); for ( int vertexPositionIndex = 0; vertexPositionIndex < vertexPositions.length; vertexPositionIndex++ ) { if ( !isClose( radius, vertexPositions, vertexPositionIndex ) ) { vertexPosition = vertexPositions[ vertexPositionIndex ]; away.add( vertexPosition ); } } return away.toArray( new Vec3[ away.size() ] ); } double getBack( Vec3[] vertexPositions ) { back = - 999999999.0; for ( int vertexIndex = 0; vertexIndex < vertexPositions.length; vertexIndex++ ) { back = Math.max( back, vertexPositions[ vertexIndex ].y ); } return back; } double getBottom( Vec3[] vertexPositions ) { double bottom = 999999999.0; for ( int vertexIndex = 0; vertexIndex < vertexPositions.length; vertexIndex++ ) { bottom = Math.min( bottom, vertexPositions[ vertexIndex ].z ); } return bottom; } /** Get buffered writer to a gcode file. @param name name of triangle mesh @return buffered writer to a gcode file */ BufferedWriter getBufferedWriter( name ) { fileDescriptor = new BFileChooser( BFileChooser.SAVE_FILE, "Select name for the slices file."); objectSlicesName = name + ".gcode"; fileDescriptor.setSelectedFile( new File( objectSlicesName ) ); fileDescriptor.showDialog( window ); filename = fileDescriptor.getSelectedFile(); if ( filename == null ) { return null; } return new BufferedWriter( new FileWriter( filename ) ); } Vec3[] getCentersFromIntersectionLoop( Vector circleIntersectionLoop ) { Vec3[] vertexPositions = new Vec3[ circleIntersectionLoop.size() ]; for ( int circleIntersectionIndex = 0; circleIntersectionIndex < circleIntersectionLoop.size(); circleIntersectionIndex++ ) { circleIntersection = circleIntersectionLoop.get( circleIntersectionIndex ); vertexPositions[ circleIntersectionIndex ] = circleIntersection.circleNodeAhead.circle; } return vertexPositions; } Vector getCentersFromIntersectionLoops( Vector circleIntersectionLoops ) { Vector centers = new Vector(); for ( int loopIndex = 0; loopIndex < circleIntersectionLoops.size(); loopIndex++ ) { circleIntersectionLoop = circleIntersectionLoops.get( loopIndex ); centers.add( getCentersFromIntersectionLoop( circleIntersectionLoop ) ); } return centers; } Vector getCentersfromLoopDirection( boolean isWiddershins, Vec3[] vertexPositions, double radius ) { Vector circleNodes = getCircleNodesFromLoop( vertexPositions, radius ); Vector circleIntersections = getCircleIntersectionsFromCircleNodes( circleNodes ); circleIntersectionLoops = getCircleIntersectionLoops( circleIntersections ); Vector centers = getCentersFromIntersectionLoops( circleIntersectionLoops ); return getLoopsfromLoopsDirection( isWiddershins, centers ); } Vector getCircleIntersectionsFromCircleNodes( Vector circleNodes ) { Vector circleIntersections = new Vector(); int index = 0; for ( int circleNodeIndex = 0; circleNodeIndex < circleNodes.size(); circleNodeIndex++ ) { circleNodeBehind = circleNodes.get( circleNodeIndex ); for ( int aheadIndex = circleNodeIndex + 1; aheadIndex < circleNodes.size(); aheadIndex++ ) { circleNodeAhead = circleNodes.get( aheadIndex ); if ( circleNodeBehind.isWithin( circleNodeAhead.circle ) ) { circleIntersectionForward = CircleIntersection().setCircleNodes( circleNodeAhead, index, circleNodeBehind ); if ( !circleIntersectionForward.isWithinCircles( circleNodes ) ) { circleIntersections.add( circleIntersectionForward ); circleNodeBehind.circleIntersections.add( circleIntersectionForward ); index++; } circleIntersectionBackward = CircleIntersection().setCircleNodes( circleNodeBehind, index, circleNodeAhead ); if ( !circleIntersectionBackward.isWithinCircles( circleNodes ) ) { circleIntersections.add( circleIntersectionBackward ); circleNodeAhead.circleIntersections.add( circleIntersectionBackward ); index++; } } } } return circleIntersections; } Vector getCircleIntersectionLoops( Vector circleIntersections ) { Vector circleIntersectionPaths = new Vector(); for ( int circleIntersectionIndex = 0; circleIntersectionIndex < circleIntersections.size(); circleIntersectionIndex++ ) { circleIntersection = circleIntersections.get( circleIntersectionIndex ); if ( !circleIntersection.steppedOn ) { Vector circleIntersectionPath = new Vector(); circleIntersectionPaths.add( circleIntersectionPath ); circleIntersection.addToVector( circleIntersectionPath ); addCircleIntersectionLoop( circleIntersectionPath, circleIntersections ); } } return circleIntersectionPaths; } Vector getCircleNodesFromLoop( Vec3[] vertexPositions, double radius ) { Vector circleNodes = new Vector(); Vector points = new Vector(); for ( int vertexPositionIndex = 0; vertexPositionIndex < vertexPositions.length; vertexPositionIndex++ ) { vertexPositionFirst = vertexPositions[ vertexPositionIndex ]; vertexPositionSecond = vertexPositions[ ( vertexPositionIndex + 1 ) % vertexPositions.length ]; points.add( vertexPositionFirst ); addPointsFromSegment( points, radius, vertexPositionFirst, vertexPositionSecond ); } vertexPositions = points.toArray( new Vec3[ points.size() ] ); vertexPositions = getAwayVertexPositions( vertexPositions, radius ); return getCircleNodesFromVertexPositions( vertexPositions, radius ); } Vector getCircleNodesFromVertexPositions( Vec3[] vertexPositions, double radius ) { Vector circleNodes = new Vector(); for ( int vertexPositionIndex = 0; vertexPositionIndex < vertexPositions.length; vertexPositionIndex++ ) { vertexPosition = vertexPositions[ vertexPositionIndex ]; circleNodes.add( CircleNode().setCircleRadius( vertexPosition, circleNodes.size(), radius ) ); } return circleNodes; } /** Get the points of the circumference of a circle. @param circleCenter center of the circle @param radius radius of the circle @return array of points */ Vec3[] getCircumference( Vec3 circleCenter, double radius ) { int circumferenceDivisions = 9; int circumferencePoints = circumferenceDivisions + 1; Vec3[] circumference = new Vec3[ circumferencePoints ]; Vec2 sideAroundZAngle = getPolar( 2.0 * Math.PI / ( double )circumferenceDivisions, 1.0 ); pointAroundCenter = new Vec3( radius, 0.0, 0.0 ); for ( int circumferencePointIndex = 0; circumferencePointIndex < circumferencePoints; circumferencePointIndex++ ) { pointAroundCenter = getRoundZAxisByPlaneAngle( sideAroundZAngle, pointAroundCenter ); circumference[ circumferencePointIndex ] = pointAroundCenter.plus( circleCenter ); } return circumference; } /** Get the points of the circumference of a circle, one a half times around. @param beginNormalized beginning around Z angle of the circle @param circleCenter center of the circle @param radius radius of the circle @return array of points */ Vec3[] getCircumferenceAndHalf( Vec2 beginNormalized, Vec3 circleCenter, double radius ) { int circumferenceDivisions = 15; int circumferencePoints = circumferenceDivisions + 1; Vec3[] circumference = new Vec3[ circumferencePoints ]; Vec2 sideAroundZAngle = getPolar( 3.0 * Math.PI / ( double )circumferenceDivisions, 1.0 ); pointAroundCenter = getRoundZAxisByPlaneAngle( beginNormalized, new Vec3( radius, 0.0, 0.0 ) ); for ( int circumferencePointIndex = 0; circumferencePointIndex < circumferencePoints; circumferencePointIndex++ ) { circumference[ circumferencePointIndex ] = pointAroundCenter.plus( circleCenter ); pointAroundCenter = getRoundZAxisByPlaneAngle( sideAroundZAngle, pointAroundCenter ); } return circumference; } /** Get the info for all the selected curves and meshes. */ ObjectInfo[] getCurvesMeshesInfo() { scene = window.getScene(); selection = scene.getSelection(); Vector objectInfoVector = new Vector(); for ( int objectIndex = 0; objectIndex < selection.length; objectIndex++ ) { objectInfo = scene.getObject( selection[ objectIndex ] ); if ( objectInfo.object instanceof TriangleMesh ) { objectInfoVector.add( objectInfo ); } } if ( objectInfoVector.size() > 0 ) { return objectInfoVector.toArray( new ObjectInfo[ objectInfoVector.size() ] ); } for ( int objectIndex = 0; objectIndex < scene.getNumObjects(); objectIndex++ ) { objectInfo = scene.getObject( objectIndex ); if ( objectInfo.object instanceof TriangleMesh ) { return new ObjectInfo[] { objectInfo }; } } return null; } double getDistanceSquaredToPlaneSegment( Vec3 segmentBegin, Vec3 segmentEnd, Vec3 point ) { Vec3 segmentDifference = segmentEnd.minus( segmentBegin ); Vec3 pointMinusSegmentBegin = point.minus( segmentBegin ); double beginPlaneDot = getPlaneDot( pointMinusSegmentBegin, segmentDifference ); if ( beginPlaneDot <= 0.0 ) { return point.distance2( segmentBegin ); } double differencePlaneDot = getPlaneDot( segmentDifference, segmentDifference ); if ( differencePlaneDot <= beginPlaneDot ) { return point.distance2( segmentEnd ); } double intercept = beginPlaneDot / differencePlaneDot; segmentDifference.scale( intercept ); Vec3 interceptPerpendicular = segmentBegin.plus( segmentDifference ); return point.distance2( interceptPerpendicular ); } Vec3[] getDoubledBack( Vec3[] vertexPositions ) { Vec3[] doubledBack = new Vec3[ vertexPositions.length + vertexPositions.length - 2 ]; int doubledBackIndex = 0; for ( int vertexPositionIndex = 0; vertexPositionIndex < vertexPositions.length; vertexPositionIndex++ ) { doubledBack[ doubledBackIndex++ ] = vertexPositions[ vertexPositionIndex ]; } for ( int vertexPositionIndex = vertexPositions.length - 2; vertexPositionIndex > 0; vertexPositionIndex-- ) { doubledBack[ doubledBackIndex++ ] = vertexPositions[ vertexPositionIndex ]; } return doubledBack; } Vector getExtruderPaths( int layer, Vec2 move, sliceGenerator ) { Vector centers = new Vector(); double doubleExtrusionWidth = 2.0 * extrusionWidth; Vector extrudateLoops = new Vector(); Vector extruderPaths = new Vector(); Vector loops = sliceGenerator.getSlice( layer ); Vector perimeterLoops = new Vector(); for ( int pathIndex = 0; pathIndex < loops.size(); pathIndex++ ) { loop = getSimpifiedVertexPositions( loops.get( pathIndex ), extrusionWidth ); perimeterLoops.add( loop ); centersFromLoopDirection = getCentersfromLoopDirection( !isWiddershins( loop ), loop, extrusionWidth ); if ( displayIntersectingCirclesCheckbox.getState() ) { Vector circleNodes = getCircleNodesFromLoop( loop, extrusionWidth ); addCircleNodesToScene( circleNodes ); } for ( int centerFromDirectionIndex = 0; centerFromDirectionIndex < centersFromLoopDirection.size(); centerFromDirectionIndex++ ) { centerFromDirection = centersFromLoopDirection.get( centerFromDirectionIndex ); if ( getMaximumSpan( centerFromDirection ) > doubleExtrusionWidth ) { centers.add( centerFromDirection ); } } } for ( int centerIndex = 0; centerIndex < centers.size(); centerIndex++ ) { center = centers.get( centerIndex ); if ( displaySliceCheckbox.getState() ) { displayPath( "Layer " + layer.toString() + " Slice Edge " + centerIndex.toString(), getMoved( move, center ) ); } extrudateLoop = getInsetFromClockwiseLoop( center, halfExtrusionWidth ); rotateLoopToFront( layer, extrudateLoop ); extrudateLoops.add( extrudateLoop ); } //if I ever join stuff again, move this below sliceFillButton.getState() for ( int extrudateLoopIndex = 0; extrudateLoopIndex < extrudateLoops.size(); extrudateLoopIndex++ ) { extrudateLoop = extrudateLoops.get( extrudateLoopIndex ); Vec3[] toBeginningExtrudateLoop = getToBeginning( extrudateLoop ); extruderPaths.add( toBeginningExtrudateLoop ); } if ( sliceFillButton.getState() ) { fill = getFill( extrudateLoops, layer, move, perimeterLoops, sliceGenerator ); extruderPaths.addAll( fill ); } if ( combHairCheckbox.getState() ) { Vector betweens = new Vector(); for ( int perimeterLoopIndex = 0; perimeterLoopIndex < perimeterLoops.size(); perimeterLoopIndex++ ) { perimeterLoop = perimeterLoops.get( perimeterLoopIndex ); centers = getCentersfromLoopDirection( !isWiddershins( perimeterLoop ), perimeterLoop, doubleExtrusionWidth ); for ( int centerIndex = 0; centerIndex < centers.size(); centerIndex++ ) { center = centers.get( centerIndex ); between = getInsetFromClockwiseLoop( center, extrusionWidth ); if ( isWiddershins( center ) == isWiddershins( between ) ) {//later, find way for to make a function for this check betweens.add( between ); } } } for ( int pathIndex = extruderPaths.size() - 2; pathIndex >= 0; pathIndex-- ) { insertPathsBetween( betweens, pathIndex, extruderPaths ); } } if ( doNotDisplayExtruderPathsButton.getState() ) { return extruderPaths; } if ( extruderPaths.size() == 0 ) { return extruderPaths; } Vector movedTravelPath = new Vector(); for ( int extruderPathIndex = 0; extruderPathIndex < extruderPaths.size(); extruderPathIndex++ ) { extruderPath = extruderPaths.get( extruderPathIndex ); if ( extruderPath.length > 1 ) { if ( movedTravelPath.size() > 0 && displayTravelCheckbox.getState() ) { movedTravelPath.add( extruderPath[ 0 ] ); travel = getTravel( movedTravelPath, extrusionWidth ); displayPath( "Layer " + layer.toString() + " Travel " + extruderPathIndex.toString(), getMoved( move, travel ) ); movedTravelPath = new Vector(); } displayPath( "Layer " + layer.toString() + " Path " + extruderPathIndex.toString(), getMovedWithArrow( halfExtrusionWidth, move, extruderPath ) ); } if ( extruderPath.length > 0 ) { movedTravelPath.add( extruderPath[ extruderPath.length - 1 ] ); } else { print( "zero length extruder path which was skipped over, this should never happen" ); } } return extruderPaths; } Vector getFill( Vector extruderLoops, int layer, Vec2 move, Vector perimeterLoops, sliceGenerator ) { Vector alreadyFilledArounds = new Vector(); Vector arounds = new Vector(); double back = - 999999999.0; Vector betweens = new Vector(); double doubleExtrusionWidth = 2.0 * extrusionWidth; Vector endpoints = new Vector(); Vector fill = new Vector(); int fillEdgeIndex = 0; double fillInset = extrusionWidth + absoluteFillPerimeterOverlap; double aroundInset = fillInset - 0.3 * halfExtrusionWidth; front = 999999999.0; Vec2 layerRotationPlaneAngle = getLayerRotationPlaneAngle( layer ); Vec2 reverseRotationPlaneAngle = new Vec2( layerRotationPlaneAngle.x, - layerRotationPlaneAngle.y ); Vector rotatedExtruderLoops = new Vector(); double stretch = 0.1 * extrusionWidth; for ( int extruderLoopIndex = 0; extruderLoopIndex < extruderLoops.size(); extruderLoopIndex++ ) { extruderLoop = extruderLoops.get( extruderLoopIndex ); planeRotated = getPlaneArrayRotatedByVector( layerRotationPlaneAngle, extruderLoop ); back = Math.max( back, getBack( planeRotated ) ); front = Math.min( front, getFront( planeRotated ) ); rotatedExtruderLoops.add( planeRotated ); } for ( int perimeterLoopIndex = 0; perimeterLoopIndex < perimeterLoops.size(); perimeterLoopIndex++ ) { Vector alreadyFilledLoop = new Vector(); alreadyFilledArounds.add( alreadyFilledLoop ); perimeterLoop = perimeterLoops.get( perimeterLoopIndex ); planeRotatedPerimeter = getPlaneArrayRotatedByVector( layerRotationPlaneAngle, perimeterLoop ); alreadyFilledLoop.add( planeRotatedPerimeter ); centers = getCentersfromLoopDirection( !isWiddershins( planeRotatedPerimeter ), planeRotatedPerimeter, doubleExtrusionWidth ); for ( int centerIndex = 0; centerIndex < centers.size(); centerIndex++ ) { center = centers.get( centerIndex ); inset = getInsetFromClockwiseLoop( center, fillInset ); if ( isWiddershins( center ) == isWiddershins( inset ) ) { around = getInsetFromClockwiseLoop( center, aroundInset ); between = getInsetFromClockwiseLoop( center, extrusionWidth ); if ( displayFillEdgesCheckbox.getState() ) { reverseRotatedInset = getPlaneArrayRotatedByVector( reverseRotationPlaneAngle, inset ); displayPath( "Layer " + layer.toString() + " Fill Edge " + fillEdgeIndex.toString(), getMovedWithArrow( halfExtrusionWidth, move, getToBeginning( reverseRotatedInset ) ) ); fillEdgeIndex++; reverseRotatedInset = getPlaneArrayRotatedByVector( reverseRotationPlaneAngle, around ); displayPath( "Layer " + layer.toString() + " Fill Edge " + fillEdgeIndex.toString(), getMovedWithArrow( halfExtrusionWidth, move, getToBeginning( reverseRotatedInset ) ) ); fillEdgeIndex++; } alreadyFilledLoop.add( inset ); arounds.add( around ); betweens.add( between ); } } } fillBack = back - halfExtrusionWidth; fillFront = front + halfExtrusionWidth; fillWidth = fillBack - fillFront; int numberOfIntervals = ( int )Math.floor( fillWidth / extrusionWidth ); double fillRemainder = fillWidth - ( double )numberOfIntervals * extrusionWidth; double halfFillRemainder = 0.5 * fillRemainder; fillBack -= halfFillRemainder; fillFront += halfFillRemainder; Vector surroundingSlices = new Vector(); int layerRemainder = layer % global.diaphragmPeriod; if ( layerRemainder >= global.diaphragmThickness ) { for ( int surroundingIndex = 1; surroundingIndex < global.solidSurfaceLayers + 1; surroundingIndex++ ) { sliceGenerator.addRotatedSlice( layer - surroundingIndex, layerRotationPlaneAngle, surroundingSlices ); sliceGenerator.addRotatedSlice( layer + surroundingIndex, layerRotationPlaneAngle, surroundingSlices ); } } Object[] horizontalSegments = new Object[ numberOfIntervals + 1 ]; for ( int fillLine = 0; fillLine < horizontalSegments.length; fillLine++ ) { y = fillFront + ( double )fillLine * extrusionWidth; horizontalSegments[ fillLine ] = getHorizontalSegments( rotatedExtruderLoops, alreadyFilledArounds, y ); } Vector removedEndpoints = new Vector(); for ( int fillLine = 0; fillLine < horizontalSegments.length; fillLine++ ) { y = fillFront + ( double )fillLine * extrusionWidth; Vector surroundingXIntersections = getSurroundingXIntersections( alreadyFilledArounds.size(), surroundingSlices, y ); addSparseEndpoints( endpoints, fillLine, horizontalSegments, removedEndpoints, surroundingXIntersections ); } if ( endpoints.size() < 1 ) { return fill; } Vector stretchedXSegments = new Vector(); int halfEndpointSize = endpoints.size() / 2; for ( int beginningIndex = 0; beginningIndex < halfEndpointSize; beginningIndex++ ) { beginningEndpoint = endpoints.get( beginningIndex + beginningIndex ); beginningPoint = beginningEndpoint.point; stretchedXSegment = StretchedXSegment().setXXY( beginningPoint.x, beginningPoint.y, beginningEndpoint.otherEndpoint.point.x, stretch ); stretchedXSegments.add( stretchedXSegment ); } endpointFirst = endpoints.get( 0 ); endpoints.remove( endpointFirst ); otherEndpoint = endpointFirst.otherEndpoint; endpoints.remove( otherEndpoint ); Vector path = new Vector(); Vector paths = new Vector(); // Vector middles = new Vector(); if ( endpoints.size() > 1 ) { nextEndpoint = otherEndpoint.getNearestMiss( arounds, endpoints, stretchedXSegments ); if ( nextEndpoint.point.distance2( endpointFirst.point ) < nextEndpoint.point.distance2( otherEndpoint.point ) ) { endpointFirst = endpointFirst.otherEndpoint; otherEndpoint = endpointFirst.otherEndpoint; } } path.add( endpointFirst.point ); path.add( otherEndpoint.point ); // middles.add( otherEndpoint.getMiddle() ); while ( endpoints.size() > 1 ) { nextEndpoint = otherEndpoint.getNearestMiss( arounds, endpoints, stretchedXSegments ); if ( nextEndpoint == null ) { paths.add( path ); path = new Vector(); smallestDistanceSquared = 999999999999999999.0; for ( int endpointIndex = 0; endpointIndex < endpoints.size(); endpointIndex++ ) { endpoint = endpoints.get( endpointIndex ); distanceSquared = endpoint.point.distance2( otherEndpoint.point ); if ( distanceSquared < smallestDistanceSquared ) { smallestDistanceSquared = distanceSquared; nextEndpoint = endpoint; } } } path.add( nextEndpoint.point ); endpoints.remove( nextEndpoint ); otherEndpoint = nextEndpoint.otherEndpoint; path.add( otherEndpoint.point ); endpoints.remove( otherEndpoint ); // middles.add( otherEndpoint.getMiddle() ); } paths.add( path ); for ( int removedIndex = 0; removedIndex < removedEndpoints.size(); removedIndex++ ) { removedEndpoint = removedEndpoints.get( removedIndex ); addAroundClosest( paths, removedEndpoint.point ); } for ( int extruderLoopIndex = extruderLoops.size() - 1; extruderLoopIndex >= 0; extruderLoopIndex-- ) { extruderLoop = extruderLoops.get( extruderLoopIndex ); if ( isWiddershins( extruderLoop ) ) { // joinInnerFillPath( extruderLoopIndex, extruderLoops, paths, rotatedExtruderLoops ); } } for ( int extruderLoopIndex = extruderLoops.size() - 1; extruderLoopIndex >= 0; extruderLoopIndex-- ) { extruderLoop = extruderLoops.get( extruderLoopIndex ); if ( !isWiddershins( extruderLoop ) ) { // joinOuterFillPath( extruderLoopIndex, extruderLoops, paths, rotatedExtruderLoops ); } } for ( int pathIndex = 0; pathIndex < paths.size(); pathIndex++ ) { path = paths.get( pathIndex ); addPath( fill, path, layer, move, reverseRotationPlaneAngle ); } return fill; } void insertPathsBetween( Vector betweens, int pathIndex, Vector paths ) { Vector betweenX = new Vector(); Vector switchX = new Vector(); Vec3[] path = paths.get( pathIndex ); int nextIndex = pathIndex + 1; Vec3[] nextPath = paths.get( nextIndex ); Vec3 pathEnd = path[ path.length - 1 ]; Vec3 nextBeginning = nextPath[ 0 ]; Vec3 segment = nextBeginning.minus( pathEnd ); segment.normalize(); Vec2 segmentXY = segment.dropAxis( 2 ); segmentYMirror = new Vec2( segment.x, - segment.y ); Vec3 pathEndRotated = getRoundZAxisByPlaneAngle( segmentYMirror, pathEnd ); Vec3 nextBeginningRotated = getRoundZAxisByPlaneAngle( segmentYMirror, nextBeginning ); double y = pathEndRotated.y; double z = pathEndRotated.z; for ( int betweenIndex = 0; betweenIndex < betweens.size(); betweenIndex++ ) { between = betweens.get( betweenIndex ); betweenRotated = getPlaneArrayRotatedByVector( segmentYMirror, between ); addXIntersections( betweenRotated, betweenIndex, switchX, y ); } Vec2[] switchXArray = getSortedSolidX( switchX ); double maximumX = Math.max( pathEndRotated.x, nextBeginningRotated.x ); double minimumX = Math.min( pathEndRotated.x, nextBeginningRotated.x ); for ( int switchIndex = 0; switchIndex < switchXArray.length; switchIndex++ ) { xIntersection = switchXArray[ switchIndex ]; if ( xIntersection.x > minimumX && xIntersection.x < maximumX ) { betweenX.add( xIntersection ); } } int halfBetweenSize = betweenX.size() / 2; for ( int betweenXIndex = betweenX.size() - 2; betweenXIndex >= 0; betweenXIndex-- ) { betweenXFirst = betweenX.get( betweenXIndex ); betweenXSecond = betweenX.get( betweenXIndex + 1 ); if ( betweenXFirst.y == betweenXSecond.y ) { while ( isLoopNumberEqual( betweenX, betweenXIndex, ( int )betweenXSecond.y ) ) { betweenXFirst = betweenX.get( betweenXIndex ); betweenXIndex--; } betweenFirst = getRoundZAxisByPlaneAngle( segmentXY, new Vec3( betweenXFirst.x, y, z ) ); betweenSecond = getRoundZAxisByPlaneAngle( segmentXY, new Vec3( betweenXSecond.x, y, z ) ); loopFirst = betweens.get( ( int )betweenXFirst.y ); addPathBetween( betweenFirst, betweenSecond, loopFirst, nextIndex, paths ); // betweenXIndex--; } } } boolean isLoopNumberEqual( Vector betweenX, int betweenXIndex, int loopNumber ) { if ( betweenXIndex < 0 ) { return false; } return betweenX.get( betweenXIndex ).y == loopNumber; } void addPathBetween( Vec3 betweenFirst, Vec3 betweenSecond, Vec3[] loopFirst, int nextIndex, Vector paths ) { Vector clockwisePath = new Vector(); Vector widdershinsPath = new Vector(); clockwisePath.add( betweenFirst ); widdershinsPath.add( betweenFirst ); Vec2 nearestFirstDistanceIndex = getNearestDistanceSquaredIndex( betweenFirst, loopFirst ); Vec2 nearestSecondDistanceIndex = getNearestDistanceSquaredIndex( betweenSecond, loopFirst ); int firstBeginIndex = ( ( int )nearestFirstDistanceIndex.y + 1 ) % loopFirst.length; int secondBeginIndex = ( ( int )nearestSecondDistanceIndex.y + 1 ) % loopFirst.length; Vector widdershinsLoop = getAroundLoop( firstBeginIndex, secondBeginIndex, loopFirst ); widdershinsPath.addAll( widdershinsLoop ); Vector clockwiseLoop = getAroundLoop( secondBeginIndex, firstBeginIndex, loopFirst ); reverseVector( clockwiseLoop ); clockwisePath.addAll( clockwiseLoop ); clockwisePath.add( betweenSecond ); widdershinsPath.add( betweenSecond ); if ( getPathLength( widdershinsPath ) > getPathLength( clockwisePath ) ) { widdershinsPath = clockwisePath; } for ( int widdershinsIndex = widdershinsPath.size() - 1; widdershinsIndex >= 0; widdershinsIndex-- ) { Vec3[] pointArray = { widdershinsPath.get( widdershinsIndex ) }; paths.insertElementAt( pointArray, nextIndex ); } } /* smallestDistanceSquared = 999999999999999999.0; nearestMiddleIndex = 0; for ( int middleIndex = 0; middleIndex < middles.size(); middleIndex++ ) { middle = middles.get( middleIndex ); for ( int endpointIndex = 0; endpointIndex < endpoints.size(); endpointIndex++ ) { endpoint = endpoints.get( endpointIndex ); distanceSquared = endpoint.point.distance2( middle ); if ( distanceSquared < smallestDistanceSquared ) { smallestDistanceSquared = distanceSquared; nearestMiddleIndex = middleIndex; nextEndpoint = endpoint; } } } for ( int middleIndex = middles.size() - 2; middleIndex >= nearestMiddleIndex; middleIndex-- ) { middle = getRoundZAxisByPlaneAngle( reverseRotationPlaneAngle, middles.get( middleIndex ) ); fill.add( new Vec3[] { middle } ); } if ( displayFillEdgesCheckbox.getState() ) { Vector middleDisplay = new Vector(); double quarterExtrusionWidth = 0.25 * extrusionWidth; for ( int middleIndex = middles.size() - 2; middleIndex >= nearestMiddleIndex; middleIndex-- ) { middle = getRoundZAxisByPlaneAngle( reverseRotationPlaneAngle, middles.get( middleIndex ) ); circumference = getCircumference( middle, quarterExtrusionWidth ); middleDisplay.addAll( Arrays.asList( circumference ) ); } displayPath( "Layer " + layer.toString() + " Fill Movement " + fill.size().toString(), getMoved( move, middleDisplay.toArray( new Vec3[ middleDisplay.size() ] ) ) ); } middles = new Vector(); path = new Vector(); */ double getFront( Vec3[] vertexPositions ) { double front = 999999999.0; for ( int vertexIndex = 0; vertexIndex < vertexPositions.length; vertexIndex++ ) { front = Math.min( front, vertexPositions[ vertexIndex ].y ); } return front; } Vec3[] getHalfSimpifiedVertexPositions( int remainder, double radius, Vec3[] vertexPositions ) { if ( vertexPositions.length < 2 ) { return vertexPositions; } Vector simplified = new Vector(); for ( int vertexPositionIndex = 0; vertexPositionIndex < vertexPositions.length; vertexPositionIndex++ ) { vertexPosition = vertexPositions[ vertexPositionIndex ]; if ( ( vertexPositionIndex % 2 ) == remainder ) { simplified.add( vertexPosition ); } else { if ( !isWithinChannel( vertexPositionIndex, vertexPositions, radius ) ) { simplified.add( vertexPosition ); } } } return simplified.toArray( new Vec3[ simplified.size() ] ); } Vector getHorizontalSegments( Vector fillLoops, Vector alreadyFilledArounds, double y ) { Vector solidXIntersectionVector = new Vector(); addXIntersectionsFromLoops( fillLoops, - 1, solidXIntersectionVector, y ); for ( int alreadyFilledAroundIndex = 0; alreadyFilledAroundIndex < alreadyFilledArounds.size(); alreadyFilledAroundIndex++ ) { alreadyFilledLoops = alreadyFilledArounds.get( alreadyFilledAroundIndex ); for ( int alreadyFilledLoopIndex = 0; alreadyFilledLoopIndex < alreadyFilledLoops.size(); alreadyFilledLoopIndex++ ) { alreadyFilledLoop = alreadyFilledLoops.get( alreadyFilledLoopIndex ); addXIntersections( alreadyFilledLoop, alreadyFilledAroundIndex, solidXIntersectionVector, y ); } } return getSegmentsFromIntersections( solidXIntersectionVector, y, fillLoops.get( 0 )[ 0 ].z ); } Vec3[] getInsetFromClockwiseLoop( Vec3[] loop, double radius ) { Vector insetLoopVector = new Vector(); for ( int loopIndex = 0; loopIndex < loop.length; loopIndex++ ) { behindAbsolute = loop[ ( loopIndex + loop.length - 1 ) % loop.length ]; center = loop[ loopIndex ]; aheadAbsolute = loop[ ( loopIndex + 1 ) % loop.length ]; behindSegment = behindAbsolute.minus( center ); behindSegment.normalize(); aheadSegment = aheadAbsolute.minus( center ); aheadSegment.normalize(); behindWiddershins = getRotatedWiddershinsQuarterAroundZAxis( behindSegment ); aheadClockwise = getRotatedClockwiseQuarterAroundZAxis( aheadSegment ); zCross = getZComponentCrossProduct( aheadSegment, behindSegment ); boolean zCrossPositive = zCross > 0.0; if ( zCrossPositive ) { bisector = behindWiddershins.plus( aheadClockwise ); // widdershinsScale = 1.0 + 0.41 * zCross; widdershinsScale = 1.0 + 0.2 * zCross; widdershinsScale = 1.0; bisector.scale( radius * widdershinsScale / bisector.length() ); bisector.add( center ); insetLoopVector.add( bisector ); } Vec3 aheadMinusBehind = aheadAbsolute.minus( center ); aheadMinusBehind.scale( 0.5 ); rotatedClockwiseQuarter = getRotatedClockwiseQuarterAroundZAxis( aheadMinusBehind ); rotatedClockwiseQuarter.scale( radius / rotatedClockwiseQuarter.length() ); aheadMinusBehind.add( rotatedClockwiseQuarter ); aheadMinusBehind.add( center ); insetLoopVector.add( aheadMinusBehind ); } simplifiedEdge = insetLoopVector.toArray( new Vec3[ insetLoopVector.size() ] ); simplifiedEdge = getSimpifiedVertexPositions( simplifiedEdge, radius ); return simplifiedEdge; } Vec2 getLayerRotationPlaneAngle( int layer ) { double fillRotationAngle = fillBeginRotation + ( double )( layer % 2 ) * fillOddLayerExtraRotation; return getPolar( - fillRotationAngle, 1.0 ); } double getLeft( Vec3[] vertexPositions ) { double left = 999999999.0; for ( int vertexIndex = 0; vertexIndex < vertexPositions.length; vertexIndex++ ) { left = Math.min( left, vertexPositions[ vertexIndex ].x ); } return left; } Vec3 getLeftVertexPosition( Vec3[] vertexPositions ) { double left = 999999999.0; Vec3 leftVertexPosition = null; for ( int vertexPositionIndex = 0; vertexPositionIndex < vertexPositions.length; vertexPositionIndex++ ) { vertexPosition = vertexPositions[ vertexPositionIndex ]; if ( vertexPosition.x < left ) { left = vertexPosition.x; leftVertexPosition = vertexPosition; } } return leftVertexPosition; } Vector getLoopsFromCorrectMesh( TriangleMesh.Edge[] edges, TriangleMesh.Face[] faces, Vec3[] vertexPositions, double z ) { Hashtable remainingEdgeTable = getRemainingEdgeTable( edges, vertexPositions, z ); Vector loops = new Vector(); pathIndexes = getPathIndexesAddPath( edges, faces, loops, remainingEdgeTable, vertexPositions, z ); while ( pathIndexes != null ) { pathIndexes = getPathIndexesAddPath( edges, faces, loops, remainingEdgeTable, vertexPositions, z ); } return loops; } Vector getLoopsfromLoopsDirection( boolean isWiddershins, Vector loops ) { Vector directionalLoops = new Vector(); for ( int loopIndex = 0; loopIndex < loops.size(); loopIndex++ ) { loop = loops.get( loopIndex ); if ( isWiddershins( loop ) == isWiddershins ) { directionalLoops.add( loop ); } } return directionalLoops; } Vector getLoopsFromMesh( TriangleMesh.Edge[] edges, TriangleMesh.Face[] faces, int layer, Vec3[] vertexPositions, double z ) { Vector loops = new Vector(); if ( correctButton.getState() ) { loops = getLoopsFromCorrectMesh( edges, faces, vertexPositions, z ); } if ( loops.size() < 1 ) { loops = getLoopsFromUnprovenMesh( edges, faces, vertexPositions, z ); } for ( int pathIndex = 0; pathIndex < loops.size(); pathIndex++ ) { path = loops.get( pathIndex ); leftVertexPosition = getLeftVertexPosition( path ); int totalNumberOfIntersectionsToLeft = 0; for ( int otherPolygonIndex = 0; otherPolygonIndex < pathIndex; otherPolygonIndex++ ) { totalNumberOfIntersectionsToLeft += getNumberOfIntersectionsToLeft( leftVertexPosition, loops.get( otherPolygonIndex ) ); } for ( int otherPolygonIndex = pathIndex + 1; otherPolygonIndex < loops.size(); otherPolygonIndex++ ) { totalNumberOfIntersectionsToLeft += getNumberOfIntersectionsToLeft( leftVertexPosition, loops.get( otherPolygonIndex ) ); } boolean polygonIsWiddershins = isWiddershins( path ); boolean isEven = ( totalNumberOfIntersectionsToLeft % 2 ) == 0; if ( isEven != polygonIsWiddershins ) { reverseArray( path ); } } return loops; } Vector getLoopsFromUnprovenMesh( TriangleMesh.Edge[] edges, TriangleMesh.Face[] faces, Vec3[] vertexPositions, double z ) { Hashtable edgePairTable = new Hashtable(); double importRadius = importCoarseness * extrusionWidth; Vector points = new Vector(); Hashtable remainingEdgeTable = getRemainingEdgeTable( edges, vertexPositions, z ); remainingEdgeTableKeys = remainingEdgeTable.keys(); while ( remainingEdgeTableKeys.hasMoreElements() ) { remainingEdgeIndexKey = remainingEdgeTableKeys.nextElement(); edge = remainingEdgeTable.get( remainingEdgeIndexKey ); sliceIntersection = getSliceIntersectionFromEdge( edge, vertexPositions, z ); points.add( sliceIntersection ); faceOne = faces[ edge.f1 ]; addEdgePair( edgePairTable, edges, faceOne.e1, remainingEdgeIndexKey, remainingEdgeTable ); addEdgePair( edgePairTable, edges, faceOne.e2, remainingEdgeIndexKey, remainingEdgeTable ); addEdgePair( edgePairTable, edges, faceOne.e3, remainingEdgeIndexKey, remainingEdgeTable ); faceTwo = faces[ edge.f2 ]; addEdgePair( edgePairTable, edges, faceTwo.e1, remainingEdgeIndexKey, remainingEdgeTable ); addEdgePair( edgePairTable, edges, faceTwo.e2, remainingEdgeIndexKey, remainingEdgeTable ); addEdgePair( edgePairTable, edges, faceTwo.e3, remainingEdgeIndexKey, remainingEdgeTable ); } edgePairValuesIterator = edgePairTable.values().iterator(); while ( edgePairValuesIterator.hasNext() ) { edgePairValue = edgePairValuesIterator.next(); edgePairValue.addPointsAtZ( points, importRadius, vertexPositions, z ); } vertexPositions = points.toArray( new Vec3[ points.size() ] ); vertexPositions = getAwayVertexPositions( vertexPositions, importRadius ); Vector allCircleNodes = getCircleNodesFromVertexPositions( vertexPositions, importRadius ); if ( displayIntersectingCirclesCheckbox.getState() ) { addCircleNodesToScene( allCircleNodes ); } Vector allCircleIntersections = getCircleIntersectionsFromCircleNodes( allCircleNodes ); allCircleIntersectionLoops = getCircleIntersectionLoops( allCircleIntersections ); centers = getCentersFromIntersectionLoops( allCircleIntersectionLoops ); return getLoopsfromLoopsDirection( importTinyDetailsCheckbox.getState(), centers ); } double getMaximumSpan( Vec3[] vertexPositions ) { xSpan = getRight( vertexPositions ) - getLeft( vertexPositions ); ySpan = getBack( vertexPositions ) - getFront( vertexPositions ); return Math.max( xSpan, ySpan ); } /** Get moved polygon. @param move polygon movement @param vertexPositions polygon vertex positions @return moved polygon */ Vec3[] getMoved( Vec2 move, Vec3[] vertexPositions ) { Vec3[] moved = new Vec3[ vertexPositions.length ]; for ( int vertexPositionIndex = 0; vertexPositionIndex < vertexPositions.length; vertexPositionIndex++ ) { vertexPosition = vertexPositions[ vertexPositionIndex ]; moved[ vertexPositionIndex ] = new Vec3( vertexPosition.x + move.x, vertexPosition.y + move.y, vertexPosition.z ); } return moved; } /** Get moved polygon with an arrow at the beginning of it. @param length length of arrow @param move polygon movement @param vertexPositions polygon vertex positions @return moved polygon */ Vec3[] getMovedWithArrow( double length, Vec2 move, Vec3[] vertexPositions ) { Vec3[] moved = getMoved( move, vertexPositions ); int movedWithArrowIndex = 0; lastVertexPosition = moved[ 0 ]; lastSegment = moved[ 1 ].minus( lastVertexPosition ); lastSegment.scale( length / lastSegment.length() ); arrow = getArrow( lastVertexPosition, lastSegment ); Vec3[] movedWithArrow = new Vec3[ vertexPositions.length + arrow.length - 1 ]; for ( int vertexPositionIndex = 0; vertexPositionIndex < arrow.length; vertexPositionIndex++ ) { movedWithArrow[ movedWithArrowIndex++ ] = arrow[ vertexPositionIndex ]; } for ( int vertexPositionIndex = 1; vertexPositionIndex < vertexPositions.length; vertexPositionIndex++ ) { movedWithArrow[ movedWithArrowIndex++ ] = moved[ vertexPositionIndex ]; } return movedWithArrow; } Vec2 getNearestDistanceSquaredIndex( Vec3 point, Vec3[] vertexPositions ) { double smallestDistanceSquared = 999999999999999999.0; Vec2 nearestDistanceSquaredIndex = null; for ( int vertexPositionIndex = 0; vertexPositionIndex < vertexPositions.length; vertexPositionIndex++ ) { segmentBegin = vertexPositions[ vertexPositionIndex ]; segmentEnd = vertexPositions[ ( vertexPositionIndex + 1 ) % vertexPositions.length ]; distanceSquared = getDistanceSquaredToPlaneSegment( segmentBegin, segmentEnd, point ); if ( distanceSquared < smallestDistanceSquared ) { smallestDistanceSquared = distanceSquared; nearestDistanceSquaredIndex = new Vec2( distanceSquared, ( double )vertexPositionIndex ); } } return nearestDistanceSquaredIndex; } Vec3 getNearestPointOnSegment( Vec3 segmentBegin, Vec3 segmentEnd, Vec3 point ) { Vec3 segmentDifference = segmentEnd.minus( segmentBegin ); Vec3 pointMinusSegmentBegin = point.minus( segmentBegin ); double beginPlaneDot = getPlaneDot( pointMinusSegmentBegin, segmentDifference ); double differencePlaneDot = getPlaneDot( segmentDifference, segmentDifference ); double intercept = beginPlaneDot / differencePlaneDot; intercept = Math.max( intercept, 0.0 ); intercept = Math.min( intercept, 1.0 ); segmentDifference.scale( intercept ); return segmentBegin.plus( segmentDifference ); } int getNextEdgeIndexAroundZ( TriangleMesh.Edge edge, TriangleMesh.Face[] faces, Hashtable remainingEdgeTable ) { TriangleMesh.Face firstFace = faces[ edge.f1 ]; TriangleMesh.Face secondFace = faces[ edge.f2 ]; if ( remainingEdgeTable.containsKey( firstFace.e1 ) ) { return firstFace.e1; } if ( remainingEdgeTable.containsKey( firstFace.e2 ) ) { return firstFace.e2; } if ( remainingEdgeTable.containsKey( firstFace.e3 ) ) { return firstFace.e3; } if ( remainingEdgeTable.containsKey( secondFace.e1 ) ) { return secondFace.e1; } if ( remainingEdgeTable.containsKey( secondFace.e2 ) ) { return secondFace.e2; } if ( remainingEdgeTable.containsKey( secondFace.e3 ) ) { return secondFace.e3; } return - 1; } int getNumberOfIntersectionsToLeft( Vec3 leftVertexPosition, Vec3[] vertexPositions ) { int numberOfIntersectionsToLeft = 0; for ( int vertexPositionIndex = 0; vertexPositionIndex < vertexPositions.length; vertexPositionIndex++ ) { firstVertexPosition = vertexPositions[ vertexPositionIndex ]; secondVertexPosition = vertexPositions[ ( vertexPositionIndex + 1 ) % vertexPositions.length ]; isLeftAboveFirst = leftVertexPosition.y > firstVertexPosition.y; isLeftAboveSecond = leftVertexPosition.y > secondVertexPosition.y; if ( isLeftAboveFirst != isLeftAboveSecond ) { if ( getXIntersection( firstVertexPosition, secondVertexPosition, leftVertexPosition.y ) < leftVertexPosition.x ) { numberOfIntersectionsToLeft++; } } } return numberOfIntersectionsToLeft; } Vec3[] getPath( TriangleMesh.Edge[] edges, Vector pathIndexes, Vec3[] vertexPositions, double z ) { Vec3[] path = new Vec3[ pathIndexes.size() ]; for ( int pathIndexIndex = 0; pathIndexIndex < pathIndexes.size(); pathIndexIndex++ ) { pathIndex = pathIndexes.get( pathIndexIndex ); edge = edges[ pathIndex ]; pathPoint = getSliceIntersectionFromEdge( edge, vertexPositions, z ); path[ pathIndexIndex ] = pathPoint; } return path; } Vector getPathIndexesAddPath( TriangleMesh.Edge[] edges, TriangleMesh.Face[] faces, Vector loops, Hashtable remainingEdgeTable, Vec3[] vertexPositions, double z ) { if ( remainingEdgeTable.size() < 1 ) { return null; } Vector pathIndexes = new Vector(); remainingEdgeIndexKey = remainingEdgeTable.keys().nextElement(); pathIndexes.add( remainingEdgeIndexKey ); remainingEdgeTable.remove( remainingEdgeIndexKey ); nextEdgeIndexAroundZ = getNextEdgeIndexAroundZ( edges[ remainingEdgeIndexKey ], faces, remainingEdgeTable ); // print( "remainingEdgeIndexKey" ); // print( remainingEdgeIndexKey ); while ( nextEdgeIndexAroundZ != - 1 ) { pathIndexes.add( nextEdgeIndexAroundZ ); remainingEdgeTable.remove( nextEdgeIndexAroundZ ); nextEdgeIndexAroundZ = getNextEdgeIndexAroundZ( edges[ nextEdgeIndexAroundZ ], faces, remainingEdgeTable ); // print( nextEdgeIndexAroundZ ); } if ( pathIndexes.size() < 3 ) { print( "Dangling edges, will use intersecting circles to get import layer at height " + z.toString() ); // print( pathIndexes ); // pathIndexZero = pathIndexes.get( 0 ); // print( pathIndexZero ); // edgeZero = edges[ pathIndexZero ]; // print( edgeZero ); // face1 = faces[ edgeZero.f1 ]; // face2 = faces[ edgeZero.f2 ]; // print( face1 ); // print( face1.e1 ); // print( face1.e2 ); // print( face1.e3 ); // print( face2 ); // print( face2.e1 ); // print( face2.e2 ); // print( face2.e3 ); loops.removeAllElements(); return null; } loops.add( getPath( edges, pathIndexes, vertexPositions, z ) ); return pathIndexes; } double getPathLength( Vector path ) { double totalLength = 0.0; for ( int pathIndex = 0; pathIndex < path.size() - 1; pathIndex++ ) { firstPoint = path.get( pathIndex ); secondPoint = path.get( pathIndex + 1 ); totalLength += firstPoint.distance( secondPoint ); } return totalLength; } Vec3[] getPlaneArrayRotatedByVector( Vec2 planeAngle, Vec3[] vertexPositions ) { Vec3[] planeArray = new Vec3[ vertexPositions.length ]; for ( int vertexPositionIndex = 0; vertexPositionIndex < vertexPositions.length; vertexPositionIndex++ ) { planeArray[ vertexPositionIndex ] = getRoundZAxisByPlaneAngle( planeAngle, vertexPositions[ vertexPositionIndex ] ); } return planeArray; } /** Get the dot product of the x and y components of a pair of Vec3s. @param vector3First first Vec3 @param vector3Second second Vec3 @return dot product of the x and y components of a pair of Vec3s */ double getPlaneDot( Vec3 vector3First, Vec3 vector3Second ) { return vector3First.x * vector3Second.x + vector3First.y * vector3Second.y; } double getPlaneDotPlusOne( Vec3 vector3First, Vec3 vector3Second ) { return 1.0 + getPlaneDot( vector3First, vector3Second ); } /** Get the amount the slice display will be moved. @param layer slice layer @param width width of the slice with margin @return amount the slice display will be moved */ Vec2 getPlaneMove( int layer, double width ) { if ( lineButton.getState() ) { return new Vec2( ( double )( layer + 1 ) * width, 0.0 ); } if ( spiralButton.getState() ) { double outward = Math.sqrt( ( double )layer + Math.PI ); double outwardMultiplied = outward / Math.sqrt( Math.PI ); return getPolar( 2.0 * Math.PI * outwardMultiplied, 1.3 * width * outwardMultiplied ); } return new Vec2(); } Vec3 getPlanePointIndex( double distance, Vec3 point, int startIndex, Vec3[] vertexPositions ) { Vec3 farthest = null; double largestDistance = - 999999999.0; for ( int vertexPositionIndex = startIndex; vertexPositionIndex < startIndex + vertexPositions.length; vertexPositionIndex++ ) { vertexIndexRemainder = vertexPositionIndex % vertexPositions.length; vertexPosition = vertexPositions[ vertexIndexRemainder ]; pointVertexDistance = point.distance( vertexPosition ); if ( pointVertexDistance > distance ) { planeVertex = vertexPosition.minus( point ); planeVertex.scale( distance / pointVertexDistance ); planeVertex.add( point ); planeVertex.z = vertexPositionIndex; return planeVertex; } if ( distance > largestDistance ) { largestDistance = distance; farthestIndex = vertexIndexRemainder; } } planeVertex = new Vec3( vertexPositions[ farthestIndex ] ); planeVertex.z = farthestIndex + 1; return planeVertex; } /** Get polar Vec2 from counterclockwise angle from 1, 0 and radius. @param angle counterclockwise angle from 1, 0 @param radius radius of vector @return polar vector */ Vec2 getPolar( double angle, double radius ) { return new Vec2( radius * Math.cos( angle ), radius * Math.sin( angle ) ); } double getPolygonArea( Vec3[] vertexPositions ) { double polygonArea = 0.0; for ( int vertexIndex = 0; vertexIndex < vertexPositions.length; vertexIndex++ ) { vertexPosition = vertexPositions[ vertexIndex ]; secondVertexPosition = vertexPositions[ ( vertexIndex + 1 ) % vertexPositions.length ]; area = vertexPosition.x * secondVertexPosition.y - secondVertexPosition.x * vertexPosition.y; polygonArea += area; } return polygonArea; } double getPolygonLength( Vec3[] vertexPositions ) { double totalLength = 0.0; for ( int vertexPositionIndex = 0; vertexPositionIndex < vertexPositions.length; vertexPositionIndex++ ) { firstVertexPosition = vertexPositions[ vertexPositionIndex ]; secondVertexPosition = vertexPositions[ ( vertexPositionIndex + 1 ) % vertexPositions.length ]; totalLength += firstVertexPosition.distance( secondVertexPosition ); } return totalLength; } Hashtable getRemainingEdgeTable( TriangleMesh.Edge[] edges, Vec3[] vertexPositions, double z ) { Hashtable remainingEdgeTable = new Hashtable(); for ( int edgeIndex = 0; edgeIndex < edges.length; edgeIndex++ ) { edge = edges[ edgeIndex ]; if ( isZInEdge( edge, vertexPositions, z ) ) { remainingEdgeTable.put( edgeIndex, edge ); } } return remainingEdgeTable; } double getRight( Vec3[] vertexPositions ) { right = - 999999999.0; for ( int vertexIndex = 0; vertexIndex < vertexPositions.length; vertexIndex++ ) { right = Math.max( right, vertexPositions[ vertexIndex ].x ); } return right; } /** Get Vec3 rotated a quarter clockwise turn around Z axis. @param vector3 Vec3 whose rotation will be returned @return vector3 rotated a quarter clockwise turn around Z axis */ Vec3 getRotatedClockwiseQuarterAroundZAxis( Vec3 vector3 ) { return new Vec3( vector3.y, - vector3.x, vector3.z ); } /** Get Vec3 rotated a quarter widdershins turn around Z axis. @param vector3 Vec3 whose rotation will be returned @return vector3 rotated a quarter widdershins turn around Z axis */ Vec3 getRotatedWiddershinsQuarterAroundZAxis( Vec3 vector3 ) { return new Vec3( - vector3.y, vector3.x, vector3.z ); } /** Get Vec3 rotated around X axis from counterclockwise angle and vector. @param angle counterclockwise angle from 1, 0 @param vector3 Vec3 whose rotation will be returned @return vector3 rotated around X axis */ Vec3 getRotatedAroundXAxis( double angle, Vec3 vector3 ) { x = Math.cos( angle ); y = Math.sin( angle ); return new Vec3( vector3.x, vector3.y * x - vector3.z * y, vector3.y * y + vector3.z * x ); } /** Get Vec3 rotated around Y axis from counterclockwise angle and vector. @param angle counterclockwise angle from 1, 0 @param vector3 Vec3 whose rotation will be returned @return vector3 rotated around Y axis */ Vec3 getRotatedAroundYAxis( double angle, Vec3 vector3 ) { x = Math.cos( angle ); y = Math.sin( angle ); return new Vec3( vector3.x * x - vector3.z * y, vector3.y, vector3.x * y + vector3.z * x ); } /** Get Vec3 rotated around Z axis from counterclockwise angle and vector. @param angle counterclockwise angle from 1, 0 @param vector3 Vec3 whose rotation will be returned @return vector3 rotated around Z axis */ Vec3 getRotatedAroundZAxis( double angle, Vec3 vector3 ) { x = Math.cos( angle ); y = Math.sin( angle ); return new Vec3( vector3.x * x - vector3.y * y, vector3.x * y + vector3.y * x, vector3.z ); } /** Get value rounded to three places as string. @param number number which will be rounded @return string rounded to three places. */ String getRoundedToThreePlaces( double number ) { return ( Math.round( 1000.0 * number ) / 1000.0 ).toString(); } TriangleMesh.Face getSharedFace( TriangleMesh.Edge firstEdge, TriangleMesh.Face[] faces, TriangleMesh.Edge secondEdge ) { if ( firstEdge.f1 == secondEdge.f1 || firstEdge.f1 == secondEdge.f2 ) { return faces[ firstEdge.f1 ]; } if ( firstEdge.f2 == secondEdge.f1 || firstEdge.f2 == secondEdge.f2 ) { return faces[ firstEdge.f2 ]; } return null; } /** Get Vec3 rotated by a plane angle. @param planeAngle plane angle of the rotation @param vector3 Vec3 whose rotation will be returned @return vector3 rotated by a plane angle */ Vec3 getRoundZAxisByPlaneAngle( Vec2 planeAngle, Vec3 vector3 ) { return new Vec3( vector3.x * planeAngle.x - vector3.y * planeAngle.y, vector3.x * planeAngle.y + vector3.y * planeAngle.x, vector3.z ); } Vector getSegmentsFromIntersections( Vector solidXIntersectionVector, double y, double z ) { Vector segments = new Vector(); Vector xIntersectionVector = new Vector(); boolean fill = false; Hashtable solidTable = new Hashtable(); boolean solid = false; Vec2[] solidXIntersectionArray = getSortedSolidX( solidXIntersectionVector ); for ( int solidXIndex = 0; solidXIndex < solidXIntersectionArray.length; solidXIndex++ ) { solidX = solidXIntersectionArray[ solidXIndex ]; solidXYInteger = ( int )solidX.y; if ( solidXYInteger >= 0 ) { toggleHashtable( solidTable, solidXYInteger, "" ); } else { fill = !fill; } oldSolid = solid; solid = ( solidTable.size() == 0 && fill ); if ( oldSolid != solid ) { xIntersectionVector.add( solidX.x ); } } for ( int xIntersectionIndex = 0; xIntersectionIndex < xIntersectionVector.size(); xIntersectionIndex += 2 ) { firstX = xIntersectionVector.get( xIntersectionIndex ); secondX = xIntersectionVector.get( xIntersectionIndex + 1 ); endpointFirst = Endpoint(); endpointSecond = Endpoint().setOtherPoint( endpointFirst, new Vec3( secondX, y, z ) ); endpointFirst.setOtherPoint( endpointSecond, new Vec3( firstX, y, z ) ); Object[] segment = { endpointFirst, endpointSecond }; segments.add( segment ); } return segments; } Vec3[] getSimpifiedVertexPositions( Vec3[] vertexPositions, double radius ) { if ( vertexPositions.length < 2 ) { return vertexPositions; } long simplificationMultiplication = 256; double simplificationRadius = radius / ( double )simplificationMultiplication; int maximumIndex = vertexPositions.length * simplificationMultiplication; for ( long vertexPositionIndex = 1; vertexPositionIndex < maximumIndex; vertexPositionIndex += vertexPositionIndex ) { vertexPositions = getHalfSimpifiedVertexPositions( 0, simplificationRadius, vertexPositions ); vertexPositions = getHalfSimpifiedVertexPositions( 1, simplificationRadius, vertexPositions ); simplificationRadius += simplificationRadius; simplificationRadius = Math.min( simplificationRadius, radius ); } return getAwayVertexPositions( vertexPositions, radius ); } Vec3 getSliceIntersectionFromEdge( TriangleMesh.Edge edge, Vec3[] vertexPositions, double z ) { firstVertex = vertexPositions[ edge.v1 ]; secondVertex = vertexPositions[ edge.v2 ]; zMinusFirst = z - firstVertex.z; up = secondVertex.z - firstVertex.z; sliceIntersection = secondVertex.minus( firstVertex ); sliceIntersection.scale( zMinusFirst / up ); sliceIntersection.add( firstVertex ); return sliceIntersection; } Vec2[] getSortedSolidX( Vector solidXIntersectionVector ) { Vec2[] solidXArray = solidXIntersectionVector.toArray( new Vec2[ solidXIntersectionVector.size() ] ); Arrays.sort( solidXArray, SolidXComparable() ); return solidXArray; } Vector getSurroundingXIntersections( int alreadyFilledSize, Vector surroundingSlices, double y ) { if ( surroundingSlices.size() < global.doubleSolidSurfaceLayers ) { return null; } Vector joinedX = new Vector(); Vector solidXIntersectionVector = new Vector(); for ( int surroundingIndex = 0; surroundingIndex < surroundingSlices.size(); surroundingIndex++ ) { surroundingSlice = surroundingSlices.get( surroundingIndex ); addXIntersectionsFromLoops( surroundingSlice, surroundingIndex, joinedX, y ); } Hashtable solidTable = new Hashtable(); boolean solid = false; Vec2[] joinedXArray = getSortedSolidX( joinedX ); for ( int solidXIndex = 0; solidXIndex < joinedXArray.length; solidXIndex++ ) { solidX = joinedXArray[ solidXIndex ]; solidXYInteger = ( int )solidX.y; toggleHashtable( solidTable, solidXYInteger, "" ); oldSolid = solid; solid = ( solidTable.size() >= global.doubleSolidSurfaceLayers ); if ( oldSolid != solid ) { solidXIntersectionVector.add( solidX.x ); } } return solidXIntersectionVector; } Vec3[] getToBeginning( Vec3[] vertexPositions ) { Vec3[] toBeginning = new Vec3[ vertexPositions.length + 1 ]; for ( int vertexPositionIndex = 0; vertexPositionIndex < vertexPositions.length; vertexPositionIndex++ ) { toBeginning[ vertexPositionIndex ] = vertexPositions[ vertexPositionIndex ]; } toBeginning[ vertexPositions.length ] = vertexPositions[ 0 ]; return toBeginning; } double getTop( Vec3[] vertexPositions ) { top = - 999999999.0; for ( int vertexIndex = 0; vertexIndex < vertexPositions.length; vertexIndex++ ) { top = Math.max( top, vertexPositions[ vertexIndex ].z ); } return top; } /** Get travel outline for when the extruder is off. @param path path of travel @param radius radius of outline @return travel outline */ Vec3[] getTravel( Vector path, double width ) { double radius = 0.2 * width; Vector travelDisplay = new Vector(); for ( int pointIndex = 0; pointIndex < path.size() - 1; pointIndex++ ) { point = path.get( pointIndex ); nextPoint = path.get( pointIndex + 1 ); beginNormalized = point.minus( nextPoint ); beginNormalized = beginNormalized.dropAxis( 2 ); beginNormalized.normalize(); travelDisplay.add( point ); Vec3 segment = nextPoint.minus( point ); segmentLength = segment.length(); int divisions = Math.ceil( 0.6 * segmentLength / width ); segment.scale( 1.0 / ( double )divisions ); Vec3 between = point.plus( segment.times( 0.5 ) ); for ( int division = 0; division < divisions; division++ ) { circumference = getCircumferenceAndHalf( beginNormalized, between, radius ); travelDisplay.addAll( Arrays.asList( circumference ) ); between.add( segment ); } travelDisplay.add( nextPoint ); } return travelDisplay.toArray( new Vec3[ travelDisplay.size() ] ); } double getXIntersection( Vec3 firstVertexPosition, Vec3 secondVertexPosition, double y ) { secondMinusFirst = secondVertexPosition.minus( firstVertexPosition ); yMinusFirst = y - firstVertexPosition.y; return yMinusFirst / secondMinusFirst.y * secondMinusFirst.x + firstVertexPosition.x; } double getWiddershinsDot( Vec3 vector3First, Vec3 vector3Second ) { double dot = getPlaneDotPlusOne( vector3First, vector3Second ); double zCross = getZComponentCrossProduct( vector3First, vector3Second ); if ( zCross >= 0.0 ) { return - dot; } return dot; } double getZComponentCrossProduct( Vec3 vector3First, Vec3 vector3Second ) { return vector3First.x * vector3Second.y - vector3First.y * vector3Second.x; } boolean isClose( double radius, Vec3[] vertexPositions, int vertexPositionIndex ) { double overlapDistance = 0.01 * radius; vertexPosition = vertexPositions[ vertexPositionIndex ]; for ( int overlapIndex = 0; overlapIndex < vertexPositionIndex; overlapIndex++ ) { overlapPoint = vertexPositions[ overlapIndex ]; distance = overlapPoint.distance( vertexPosition ); if ( distance < overlapDistance ) { return true; } } return false; } boolean isLineCrossingInsideXSegment( double segmentFirstX, double segmentSecondX, Vec3 vector3First, Vec3 vector3Second, double y ) { isYAboveFirst = y > vector3First.y; isYAboveSecond = y > vector3Second.y; if ( isYAboveFirst == isYAboveSecond ) { return false; } xIntersection = getXIntersection( vector3First, vector3Second, y ); if ( xIntersection <= Math.min( segmentFirstX, segmentSecondX ) ) { return false; } return xIntersection < Math.max( segmentFirstX, segmentSecondX ); } boolean isSegmentAround( Vector aroundSegments, Object[] segment ) { for ( int aroundSegmentIndex = 0; aroundSegmentIndex < aroundSegments.size(); aroundSegmentIndex++ ) { endpoint = aroundSegments.get( aroundSegmentIndex )[ 0 ]; if ( isSegmentInX( segment, endpoint.point.x, endpoint.otherEndpoint.point.x ) ) { return true; } } return false; } boolean isSegmentCompletelyInX( Object[] segment, double xFirst, xSecond ) { segmentFirstX = segment[ 0 ].point.x; segmentSecondX = segment[ 1 ].point.x; if ( Math.max( segmentFirstX, segmentSecondX ) > Math.max( xFirst, xSecond ) ) { return false; } return Math.min( segmentFirstX, segmentSecondX ) >= Math.min( xFirst, xSecond ); } boolean isSegmentInX( Object[] segment, double xFirst, xSecond ) { segmentFirstX = segment[ 0 ].point.x; segmentSecondX = segment[ 1 ].point.x; if ( Math.min( segmentFirstX, segmentSecondX ) > Math.max( xFirst, xSecond ) ) { return false; } return Math.max( segmentFirstX, segmentSecondX ) > Math.min( xFirst, xSecond ); } boolean isWiddershins( Vec3[] vertexPositions ) { return getPolygonArea( vertexPositions ) > 0.0; } boolean isWithinChannel( int vertexPositionIndex, Vec3[] vertexPositions, double radius ) { double channelRadius = radius * .01; vertexPosition = vertexPositions[ vertexPositionIndex ]; behindPosition = vertexPositions[ ( vertexPositionIndex + vertexPositions.length - 1 ) % vertexPositions.length ]; behindSegment = behindPosition.minus( vertexPosition ); behindSegmentLength = behindSegment.length(); if ( behindSegmentLength < channelRadius ) { return true; } aheadPosition = vertexPositions[ ( vertexPositionIndex + 1 ) % vertexPositions.length ]; aheadSegment = aheadPosition.minus( vertexPosition ); aheadSegmentLength = aheadSegment.length(); if ( aheadSegmentLength < channelRadius ) { return true; } behindSegment.normalize(); aheadSegment.normalize(); // absoluteZ = Math.abs( getZComponentCrossProduct( aheadSegment, behindSegment ) ); absoluteZ = getPlaneDotPlusOne( aheadSegment, behindSegment ); if ( behindSegmentLength * absoluteZ < channelRadius ) { return true; } if ( aheadSegmentLength * absoluteZ < channelRadius ) { return true; } return false; } boolean isZInEdge( TriangleMesh.Edge edge, Vec3[] vertexPositions, double z ) { double vertex1Z = vertexPositions[ edge.v1 ].z; double vertex2Z = vertexPositions[ edge.v2 ].z; boolean vertex1ZHigher = vertex1Z > z; boolean vertex2ZHigher = vertex2Z > z; return vertex1ZHigher != vertex2ZHigher; } void joinInnerFillPath( int extruderLoopIndex, Vector extruderLoops, Vector paths, Vector rotatedExtruderLoops ) { lastPath = null; lastPathPointIndex = null; smallestDistanceSquared = 1.21 * extrusionWidth * extrusionWidth; nearestPath = null; pathLoopExitIndex = 0; rotatedLoopExitIndex = 0; rotatedLoop = rotatedExtruderLoops.get( extruderLoopIndex ); for ( int pathIndex = 0; pathIndex < paths.size(); pathIndex++ ) { path = paths.get( pathIndex ); for ( int pathPointIndex = 0; pathPointIndex < path.size(); pathPointIndex++ ) { pathPoint = path.get( pathPointIndex ); Vec2 nearestDistanceSquaredIndex = getNearestDistanceSquaredIndex( pathPoint, rotatedLoop ); if ( nearestDistanceSquaredIndex.x < smallestDistanceSquared ) { if ( path == lastPath && pathPointIndex - 1 == lastPathPointIndex ) { smallestDistanceSquared = nearestDistanceSquaredIndex.x; nearestPath = path; pathLoopExitIndex = pathPointIndex; rotatedLoopExitIndex = ( int )nearestDistanceSquaredIndex.y; } lastPath = path; lastPathPointIndex = pathPointIndex; } } } //print( nearestPath ); //print( nearestPath.size() ); if ( nearestPath == null ) { return; } int pathLoopEntryIndex = pathLoopExitIndex - 1; //print( pathLoopEntryIndex ); //print( pathLoopExitIndex ); //print( "rotatedLoopExitIndex" ); //print( rotatedLoopExitIndex ); Vec3 pathLoopEntryPoint = nearestPath.get( pathLoopEntryIndex ); Vec3 pathLoopExitPoint = nearestPath.get( pathLoopExitIndex ); int rotatedLoopEntryIndex = ( int )getNearestDistanceSquaredIndex( pathLoopEntryPoint, rotatedLoop ).y; int rotatedLoopExitIndex = ( int )getNearestDistanceSquaredIndex( pathLoopExitPoint, rotatedLoop ).y; //print( "rotatedLoopEntryIndex" ); //print( rotatedLoopEntryIndex ); //print( "rotatedLoopExitIndex" ); //print( rotatedLoopExitIndex ); if ( rotatedLoopEntryIndex == rotatedLoopExitIndex ) { segmentBegin = rotatedLoop[ rotatedLoopEntryIndex ]; segmentEnd = rotatedLoop[ ( rotatedLoopEntryIndex + 1 ) % rotatedLoop.length ]; rotatedNearestLoopEntryPoint = getNearestPointOnSegment( segmentBegin, segmentEnd, pathLoopEntryPoint ); rotatedNearestLoopExitPoint = getNearestPointOnSegment( segmentBegin, segmentEnd, pathLoopExitPoint ); if ( segmentBegin.distance( rotatedNearestLoopEntryPoint ) < segmentBegin.distance( rotatedNearestLoopExitPoint ) ) { reverseArray( rotatedLoop ); } } else { if ( rotatedLoopEntryIndex < rotatedLoopExitIndex ) { reverseArray( rotatedLoop ); } } // calculating again in case rotatedLoop was reversed rotatedLoopExitIndex = ( int )getNearestDistanceSquaredIndex( pathLoopExitPoint, rotatedLoop ).y; Vector nearestFillVector = new Vector(); nearestFillVector.addAll( nearestPath.subList( 0, pathLoopExitIndex ) ); addFromAway( rotatedLoop, rotatedLoopExitIndex, pathLoopExitPoint, nearestFillVector ); nearestFillVector.addAll( nearestPath.subList( pathLoopExitIndex, nearestPath.size() ) ); int indexOfNearest = paths.indexOf( nearestPath ); paths.set( indexOfNearest, nearestFillVector ); extruderLoops.removeElementAt( extruderLoopIndex ); rotatedExtruderLoops.removeElementAt( extruderLoopIndex ); } void joinOuterFillPath( int extruderLoopIndex, Vector extruderLoops, Vector paths, Vector rotatedExtruderLoops ) { smallestDistanceToBeginSquared = 999999999999999999.0; thresholdSquared = 1.69 * extrusionWidth * extrusionWidth; nearestPath = null; nearestPathPoint = null; nearestPathPointIndex = null; nearestRotatedPointIndex = 0; rotatedLoop = rotatedExtruderLoops.get( extruderLoopIndex ); Vec3 beginPoint = rotatedLoop[ 0 ]; for ( int pathIndex = 0; pathIndex < paths.size(); pathIndex++ ) { path = paths.get( pathIndex ); for ( int pathPointIndex = 0; pathPointIndex < path.size(); pathPointIndex += path.size() - 1 ) { pathPoint = path.get( pathPointIndex ); Vec2 nearestDistanceSquaredIndex = getNearestDistanceSquaredIndex( pathPoint, rotatedLoop ); if ( nearestDistanceSquaredIndex.x < thresholdSquared ) { distanceToBeginSquared = pathPoint.distance2( beginPoint ); if ( distanceToBeginSquared < smallestDistanceToBeginSquared ) { smallestDistanceToBeginSquared = distanceToBeginSquared; nearestPath = path; nearestPathPoint = pathPoint; nearestPathPointIndex = pathPointIndex; nearestRotatedPointIndex = ( int )nearestDistanceSquaredIndex.y; } } } } if ( nearestPath == null ) { return; } if ( nearestPathPointIndex > 0 ) { reverseVector( nearestPath ); } Vector nearestFillVector = new Vector(); addFromAway( rotatedLoop, nearestRotatedPointIndex, nearestPathPoint, nearestFillVector ); nearestFillVector.addAll( nearestPath ); paths.remove( nearestPath ); int firstInsideIndex = getFirstInsideIndex( paths, rotatedLoop ); paths.insertElementAt( nearestFillVector, firstInsideIndex ); extruderLoops.removeElementAt( extruderLoopIndex ); rotatedExtruderLoops.removeElementAt( extruderLoopIndex ); } int getFirstInsideIndex( Vector paths, Vec3[] rotatedLoop ) { for ( int pathIndex = 0; pathIndex < paths.size(); pathIndex++ ) { path = paths.get( pathIndex ); numberOfIntersectionsToLeft = getNumberOfIntersectionsToLeft( path.get( 0 ), rotatedLoop ); boolean isEven = ( numberOfIntersectionsToLeft % 2 ) == 0; if ( !isEven ) { return pathIndex; } } return 0; } /** Output the curve and/or mesh. */ long outputCurveMesh( ObjectInfo objectInfo ) { long startTime = System.currentTimeMillis(); // for later, only edges and faces are used now className = objectInfo.object.getClass().getName(); classNameEnd = className.substring( className.lastIndexOf( "." ) + 1 ); name = objectInfo.name; origin = objectInfo.coords.getOrigin(); orientation = objectInfo.coords.getRotationAngles(); faces = objectInfo.object.getFaces(); edges = objectInfo.object.getEdges(); vertexPositions = objectInfo.object.getVertexPositions(); for ( int vertexPositionIndex = 0; vertexPositionIndex < vertexPositions.length; vertexPositionIndex++ ) { vertexPosition = vertexPositions[ vertexPositionIndex ]; vertexPosition = getRotatedAroundZAxis( - orientation[ 2 ] * Math.PI / 180.0, vertexPosition ); vertexPosition = getRotatedAroundXAxis( - orientation[ 0 ] * Math.PI / 180.0, vertexPosition ); vertexPosition = getRotatedAroundYAxis( orientation[ 1 ] * Math.PI / 180.0, vertexPosition ); vertexPosition.add( origin ); vertexPositions[ vertexPositionIndex ] = vertexPosition; } sliceGenerator = SliceGenerator().setEdgesFacesVertexPositions( edges, faces, vertexPositions ); width = 1.2 * getMaximumSpan( vertexPositions ); outputGCodeInitialization(); for ( int layer = slicesBeginning; layer < slicesMaximumNumber + slicesBeginning && sliceGenerator.getSlice( layer ) != null; layer++ ) { Vec2 move = getPlaneMove( layer, width ); extruderPaths = getExtruderPaths( layer, move, sliceGenerator ); outputGCodeExtruderPaths( extruderPaths, layer, name ); if ( bufferedWriter != null ) { //Flush the output stream so something will be saved even if the program crashes. bufferedWriter.flush(); Thread.yield(); } } outputGCodeShutDown(); return System.currentTimeMillis() - startTime; } /** Output the curves and/or meshes. */ void outputCurvesMeshes( ObjectInfo[] objectInfoArray ) { if ( objectInfoArray.length < 1 ) { return; } allStartTime = System.currentTimeMillis(); Vector sliceDiceTimes = new Vector(); sliceDiceTimes.add( outputCurveMesh( objectInfoArray[ 0 ] ) ); for ( int objectIndex = 1; objectIndex < objectInfoArray.length; objectIndex++ ) { outputString( "" ); sliceDiceTimes.add( outputCurveMesh( objectInfoArray[ objectIndex ] ) ); } if ( bufferedWriter != null ) { //Close the output stream bufferedWriter.close(); } for ( int sliceTimeIndex = 0; sliceTimeIndex < sliceDiceTimes.size(); sliceTimeIndex++ ) { sliceDiceTime = sliceDiceTimes.get( sliceTimeIndex ); printSliceDiceTime( sliceDiceTime, objectInfoArray[ sliceTimeIndex ].name ); } if ( objectInfoArray.length > 1 ) { printSliceDiceTime( System.currentTimeMillis() - allStartTime, "all objects" ); } } void outputGCodeExtruderPaths( Vector extruderPaths, int layer, String name ) { outputString( "( Extruder paths for layer " + layer.toString() + " of " + name + " )" ); // GCode formatted comment outputString( "M113" ); // Indicate that a new layer is starting. for ( int pathIndex = 0; pathIndex < extruderPaths.size(); pathIndex++ ) { vertexPositions = extruderPaths.get( pathIndex ); outputGCodeFromPath( vertexPositions ); } } void outputGCodeFromPath( Vec3[] vertexPositions ) { if ( vertexPositions.length > 0 ) { outputGCodeMovement( vertexPositions[ 0 ] ); } else { print( "zero length vertex positions array which was skipped over, this should never happen" ); } outputString( "M101" ); // Turn extruder on. for ( int vertexPositionIndex = 1; vertexPositionIndex < vertexPositions.length; vertexPositionIndex++ ) { vertexPosition = vertexPositions[ vertexPositionIndex ]; outputGCodeMovement( vertexPosition ); } outputString( "M103" ); // Turn extruder off. } void outputGCodeInitialization() { outputString( "( GCode generated by April 20, 2007 Skeinforge )" ); // GCode formatted comment outputString( "( Extruder Initialization )" ); // GCode formatted comment outputString( "M100 P210" ); // Set extruder speed to 210. outputString( "M103" ); // Turn extruder off. outputString( "M105" ); //Custom code for temperature reading. outputString( "M108 P" + getRoundedToThreePlaces( extrusionDiameter ) ); // Set extrusion diameter. outputString( "M109 P" + getRoundedToThreePlaces( extrusionWidth ) ); // Set extrusion width. outputString( "M110 P" + getRoundedToThreePlaces( layerThickness ) ); // Set layer thickness. outputString( "G21" ); // Set units to mm. outputString( "G90" ); // Set positioning to absolute. outputString( "G28" ); // Start at home. outputString( "M111 Pslice" ); // The skein has been sliced. outputString( "M111 Pfill" ); // The skein has been filled. outputString( "M112" ); // Initialization is finished, extrusion is starting. outputString( "( Extruder Movement )" ); // GCode formatted comment //http://forums.reprap.org/file.php?12,file=565 } void outputGCodeMovement( Vec3 absolutePosition ) { gcodeMovementLine = "G1 X" + getRoundedToThreePlaces( absolutePosition.x ) + " Y" + getRoundedToThreePlaces( absolutePosition.y ) + " Z" + getRoundedToThreePlaces( absolutePosition.z ); gcodeMovementLine += " F" + feedratePerMinute.toString(); outputString( gcodeMovementLine ); } void outputGCodeShutDown() { outputString( "( Extruder Shut Down )" ); // GCode formatted comment outputString( "M103" ); // Turn extruder off. } /** Output comma separated strings followed by a linefeed. */ void outputString( string ) { if ( selectionPrintCheckbox.getState() ) { print( string ); } if ( bufferedWriter != null ) { bufferedWriter.write( string.replace( ", ", "," ) + "\n" ); } } /** Add radio button groups to the preference widgets. @param radioButtonGroups radio button groups which will be added to the memorable widgets @param widgetVector memorable widgets */ void preferencesAddRadioButtonGroups( RadioButtonGroup[] radioButtonGroups, Vector widgetVector ) { for ( int radioIndex = 0; radioIndex < radioButtonGroups.length; radioIndex++ ) { radioButtonGroup = radioButtonGroups[ radioIndex ]; radioButtonGroupIterator = radioButtonGroup.getRadioButtons(); while ( radioButtonGroupIterator.hasNext() ) { radioButton = radioButtonGroupIterator.next(); preferencesAddWidgetWithString( radioButton, radioButton.getText(), widgetVector ); } } } /** Add widgets which have titles. @param widgets widgets which have titles @param widgetStrings widget titles @param widgetVector memorable widgets */ void preferencesAddWidgetsWithStrings( Widget[] widgets, String[] widgetStrings, Vector widgetVector ) { for ( int widgetIndex = 0; widgetIndex < widgets.length; widgetIndex++ ) { widget = widgets[ widgetIndex ]; if ( widget instanceof BCheckBox || widget instanceof BOutline || widget instanceof BTextField || widget instanceof ValueField ) { preferencesAddWidgetWithString( widget, widgetStrings[ widgetIndex ], widgetVector ); } } } /** Give the widget a name and add it to the widget vector. @param widget widget which will be given a name @param widgetStrings widget name @param widgetVector memorable widgets */ void preferencesAddWidgetWithString( Widget widget, String widgetString, Vector widgetVector ) { widget.setName( widgetString ); widgetVector.add( widget ); } /** Read widget settings from preferences file. @param preferencesFilename preferences filename @param widgetVector memorable widgets */ void preferencesRead( String preferencesFilename, Vector widgetVector ) { preferencesFile = new File( preferencesFilename ); if ( !preferencesFile.canRead() ) { return; } BufferedReader preferencesReader = new BufferedReader( new FileReader( preferencesFile ) ); line = preferencesReader.readLine(); while ( line != null ) { preferencesReadLine( line, widgetVector ); line = preferencesReader.readLine(); } } /** Read line of preferences and set widget to that line. @param line line of preferences @param widgetVector memorable widgets */ void preferencesReadLine( String line, Vector widgetVector ) { splitLine = line.split( "\t" ); if ( splitLine.length < 2 ) { return; } name = splitLine[ 0 ]; for ( int widgetIndex = 0; widgetIndex < widgetVector.size(); widgetIndex++ ) { widget = widgetVector.get( widgetIndex ); if ( widget.getName().equals( name ) ) { preferencesReadWidget( splitLine[ 1 ], widget ); return; } } } /** Set widget to preferences value. @param value preferences value @param widget widget to be set to value */ void preferencesReadWidget( String value, Widget widget ) { if ( widget instanceof BCheckBox || widget instanceof BRadioButton ) { widget.setState( Boolean.valueOf( value ) ); return; } if ( widget instanceof BOutline ) { // it would be better to save the value instead of index because the list might change, but I'm lazy bList = widget.getContent().getContent(); selectedIndex = Integer.valueOf( value ); bList.setSelected( selectedIndex, true ); bList.scrollToItem( selectedIndex ); return; } if ( widget instanceof BTextField ) { widget.setText( value ); return; } if ( widget instanceof ValueField ) { widget.setValue( Double.valueOf( value ) ); } } /** Write widget settings to preferences file. @param preferencesFilename preferences filename @param widgetVector memorable widgets */ void preferencesWrite( String preferencesFilename, Vector widgetVector ) { preferencesFile = new File( preferencesFilename ); if ( preferencesFile == null ) { print( "Can not write preferences to " + preferencesFilename ); return; } BufferedWriter preferencesWriter = new BufferedWriter( new FileWriter( preferencesFile ) ); for ( int widgetIndex = 0; widgetIndex < widgetVector.size(); widgetIndex++ ) { widget = widgetVector.get( widgetIndex ); preferencesWriteWidget( preferencesWriter, widget ); } //Close the output stream preferencesWriter.close(); } /** Write widget settings to line of preferences. @param preferencesWriter buffered preferences file writer @param widget widget to be written */ void preferencesWriteWidget( BufferedWriter preferencesWriter, Widget widget ) { widgetString = widget.getName() + "\t"; if ( widget instanceof BCheckBox || widget instanceof BRadioButton ) { preferencesWriter.write( widgetString + widget.getState().toString() + "\n" ); return; } if ( widget instanceof BOutline ) { // it would be better to save the value because the list might change, but I'm lazy BList bList = widget.getContent().getContent(); bList = widget.getContent().getContent(); preferencesWriter.write( widgetString + bList.getSelectedIndex().toString() + "\n" ); return; } if ( widget instanceof BTextField ) { preferencesWriter.write( widgetString + widget.getText() + "\n" ); return; } if ( widget instanceof ValueField ) { preferencesWriter.write( widgetString + widget.getValue().toString() + "\n" ); } } void printSliceDiceTime( long milliseconds, String name ) { seconds = ( long )Math.round( ( double )milliseconds / 1000.0 ); print( "It took " + seconds.toString() + " seconds to slice and dice " + name + "." ); } /** Reverse an array of objects. */ void reverseArray( Object[] objects ) { for ( int objectIndex = 0; objectIndex < objects.length / 2; objectIndex++ ) { oldObject = objects[ objectIndex ]; fromEnd = objects.length - objectIndex - 1; objects[ objectIndex ] = objects[ fromEnd ]; objects[ fromEnd ] = oldObject; } } /** Reverse a vector. */ void reverseVector( Vector vector ) { for ( int objectIndex = 0; objectIndex < vector.size() / 2; objectIndex++ ) { oldObject = vector.get( objectIndex ); fromEnd = vector.size() - objectIndex - 1; vector.set( objectIndex, vector.get( fromEnd ) ); vector.set( fromEnd, oldObject ); } } void rotateLoopToFront( int layer, Vec3[] loop ) { double lowestY = 999999999.0; layerRotationPlaneAngle = getLayerRotationPlaneAngle( layer ); planeRotated = getPlaneArrayRotatedByVector( layerRotationPlaneAngle, loop ); left = getLeft( planeRotated ); int startIndex = 0; right = getRight( planeRotated ); center = 0.5 * ( left + right ); for ( int vertexIndex = 0; vertexIndex < planeRotated.length; vertexIndex++ ) { vertex = planeRotated[ vertexIndex ]; x = vertex.x; indexBefore = ( vertexIndex + planeRotated.length - 1 ) % planeRotated.length; vertexBefore = planeRotated[ indexBefore ]; xBefore = vertexBefore.x; boolean xRight = x > center; boolean xBeforeRight = xBefore > center; if ( xRight != xBeforeRight ) { if ( vertex.y < lowestY ) { lowestY = vertex.y; startIndex = vertexIndex; } if ( vertexBefore.y < lowestY ) { lowestY = vertexBefore.y; startIndex = indexBefore; } } } Vector aroundLoop = getAroundLoop( startIndex, startIndex, loop ); for ( int vertexIndex = 0; vertexIndex < aroundLoop.size(); vertexIndex++ ) { loop[ vertexIndex ] = aroundLoop.get( vertexIndex ); } } SliceGenerator() { double bottom = 0.0; TriangleMesh.Edge[] edges = null; TriangleMesh.Face[] faces = null; double halfThickness = layerThickness / 2; double layerBottom = 0.0; double layerTop = 0.0; Hashtable sliceTable = new Hashtable(); double top = 0.0; Vec3[] vertexPositions = null; int zZoneLayers = 99; zZoneInterval = layerThickness / zZoneLayers / 100.0; void addRotatedSlice( int layer, Vec2 layerRotationPlaneAngle, Vector surroundingSlices ) { surroundingSlice = getSlice( layer ); if ( surroundingSlice == null ) { return; } rotatedSlice = new Vector(); for ( int loopIndex = 0; loopIndex < surroundingSlice.size(); loopIndex++ ) { loop = surroundingSlice.get( loopIndex ); planeRotatedLoop = getPlaneArrayRotatedByVector( layerRotationPlaneAngle, loop ); rotatedSlice.add( planeRotatedLoop ); } surroundingSlices.add( rotatedSlice ); } addToZoneArray( Vec3 vertexPosition, int[] zoneArray, double z ) { int zoneLayer = ( int )Math.round( ( vertexPosition.z - z ) / zZoneInterval ); int zoneAround = 2 * ( int )Math.abs( zoneLayer ); if ( zoneLayer < 0 ) { zoneAround--; } if ( zoneAround < zoneArray.length ) { zoneArray[ zoneAround ] += 1; } } getLowestZoneIndex( int[] zoneArray, double z ) { int lowestZoneIndex = 0; int lowestZone = 99999999.0; for ( int zoneIndex = 0; zoneIndex < zoneArray.length; zoneIndex++ ) { zone = zoneArray[ zoneIndex ]; if ( zone < lowestZone ) { lowestZone = zone; lowestZoneIndex = zoneIndex; } } return lowestZoneIndex; } getSlice( int layer ) { if ( layer < 0 ) { return null; } if ( sliceTable.containsKey( layer ) ) { return sliceTable.get( layer ); } z = layerBottom + ( double )layer * layerThickness; if ( z > layerTop ) { return null; } int[] zoneArray = new int[ zZoneLayers ]; for ( int vertexPositionIndex = 0; vertexPositionIndex < vertexPositions.length; vertexPositionIndex++ ) { vertexPosition = vertexPositions[ vertexPositionIndex ]; addToZoneArray( vertexPosition, zoneArray, z ); } lowestZoneIndex = getLowestZoneIndex( zoneArray, z ); zAround = getZAround( lowestZoneIndex, z ); slice = getLoopsFromMesh( edges, faces, layer, vertexPositions, zAround ); sliceTable.put( layer, slice ); return slice; } getSliceArea( int layer ) { slice = getSlice( layer ); if ( slice == null ) { return 0.0; } double totalArea = 0.0; for ( int loopIndex = 0; loopIndex < slice.size(); loopIndex++ ) { loop = slice.get( loopIndex ); totalArea += getPolygonArea( loop ); } return totalArea; } getZAround( int around, double z ) { int halfAround = ( int )Math.ceil( ( double )around / 2.0 ); zAround = ( double )halfAround * zZoneInterval; if ( around % 2 == 1 ) { zAround = - zAround; } return z + zAround; } setEdgesFacesVertexPositions( TriangleMesh.Edge[] inputEdges, TriangleMesh.Face[] inputFaces, Vec3[] inputVertexPositions ) { vertexPositions = inputVertexPositions; edges = inputEdges; faces = inputFaces; bottom = getBottom( vertexPositions ); layerBottom = bottom + halfThickness; top = getTop( vertexPositions ); layerTop = top - halfThickness * 0.5; return this; } toString() { return sliceTable.toString(); } return this; } SolidXComparable() { int compare( Vec2 solidXFirst, solidXSecond ) { if ( solidXFirst.x > solidXSecond.x ) { return 1; } if ( solidXFirst.x < solidXSecond.x ) { return - 1; } return 0; } return this; } void toggleHashtable( Hashtable hashtable, Object key, Object value ) { if ( hashtable.containsKey( key ) ) { hashtable.remove( key ); } else { hashtable.put( key, value ); } } // Set default parameters. displayRadioButtonGroup = new RadioButtonGroup(); doNotDisplayExtruderPathsButton = new BRadioButton( "Do not Display Extruder Paths", false, displayRadioButtonGroup ); lineButton = new BRadioButton( "Extruder Paths in Line", false, displayRadioButtonGroup ); placeButton = new BRadioButton( "Extruder Paths in Place", false, displayRadioButtonGroup ); spiralButton = new BRadioButton( "Extruder Paths in Spiral", true, displayRadioButtonGroup ); displayGridContainer = new GridContainer( 1, 4 ); displayGridContainer.setDefaultLayout( new LayoutInfo( LayoutInfo.WEST, LayoutInfo.NONE, new Insets( 2, 2, 2, 2 ), null ) ); displayGridContainer.add( doNotDisplayExtruderPathsButton, 0, 0 ); displayGridContainer.add( lineButton, 0, 1 ); displayGridContainer.add( placeButton, 0, 2 ); displayGridContainer.add( spiralButton, 0, 3 ); fillRadioButtonGroup = new RadioButtonGroup(); sliceButton = new BRadioButton( "Slice", false, fillRadioButtonGroup ); sliceFillButton = new BRadioButton( "Slice and Fill", true, fillRadioButtonGroup ); fillGridContainer = new GridContainer( 1, 2 ); fillGridContainer.setDefaultLayout( new LayoutInfo( LayoutInfo.WEST, LayoutInfo.NONE, new Insets( 2, 2, 2, 2 ), null ) ); fillGridContainer.add( sliceButton, 0, 0 ); fillGridContainer.add( sliceFillButton, 0, 1 ); importRadioButtonGroup = new RadioButtonGroup(); correctButton = new BRadioButton( "Correct Mesh", true, importRadioButtonGroup ); unprovenButton = new BRadioButton( "Unproven Mesh", false, importRadioButtonGroup ); importGridContainer = new GridContainer( 1, 2 ); importGridContainer.setDefaultLayout( new LayoutInfo( LayoutInfo.WEST, LayoutInfo.NONE, new Insets( 2, 2, 2, 2 ), null ) ); importGridContainer.add( correctButton, 0, 0 ); importGridContainer.add( unprovenButton, 0, 1 ); bufferedWriter = null; combHairCheckbox = new BCheckBox( "", true ); diaphragmPeriodValueField = new ValueField( 9.0, ValueField.NONNEGATIVE ); diaphragmThicknessValueField = new ValueField( 2.0, ValueField.NONNEGATIVE ); displayFillEdgesCheckbox = new BCheckBox( "", false ); displayIntersectingCirclesCheckbox = new BCheckBox( "", false ); displaySliceCheckbox = new BCheckBox( "", false ); displayTravelCheckbox = new BCheckBox( "", false ); extrusionDiameterValueField = new ValueField( 0.8, ValueField.POSITIVE ); extrusionFillDensityValueField = new ValueField( 0.9, ValueField.POSITIVE ); extrusionWidthOverThicknessValueField = new ValueField( 1.0, ValueField.NONNEGATIVE ); feedratePerSecondValueField = new ValueField( 10, ValueField.NONNEGATIVE ); fillBeginRotationValueField = new ValueField( 45, ValueField.NONE ); fillDensityValueField = new ValueField( 0.5, ValueField.NONNEGATIVE ); fillOddLayerExtraRotationValueField = new ValueField( 90, ValueField.NONE ); infillPerimeterOverlapValueField = new ValueField( 0.1, ValueField.NONE ); importCoarsenessValueField = new ValueField( 1.0, ValueField.NONNEGATIVE ); importTinyDetailsCheckbox = new BCheckBox( "", true ); String preferencesFilename = "skeinforge_preferences.csv"; selectionExportCheckbox = new BCheckBox( "", true ); selectionPrintCheckbox = new BCheckBox( "", true ); slicesBeginningValueField = new ValueField( 0.0, ValueField.NONNEGATIVE ); slicesMaximumNumberValueField = new ValueField( 9999999.0, ValueField.NONNEGATIVE ); solidSurfaceLayersValueField = new ValueField( 2.0, ValueField.NONNEGATIVE ); //temperatureValueField = new ValueField( 145, ValueField.NONE ); Widget[] widgets = new Widget[] { combHairCheckbox, diaphragmPeriodValueField, diaphragmThicknessValueField, displayFillEdgesCheckbox, displayIntersectingCirclesCheckbox, displaySliceCheckbox, displayTravelCheckbox, displayGridContainer, extrusionDiameterValueField, extrusionFillDensityValueField, extrusionWidthOverThicknessValueField, feedratePerSecondValueField, fillGridContainer, fillBeginRotationValueField, fillDensityValueField, fillOddLayerExtraRotationValueField, infillPerimeterOverlapValueField, importCoarsenessValueField, importGridContainer, importTinyDetailsCheckbox, selectionExportCheckbox, selectionPrintCheckbox, slicesBeginningValueField, slicesMaximumNumberValueField, solidSurfaceLayersValueField }; String[] widgetStrings = new String[] { "Comb Hair:", "Diaphragm Period (layers):", "Diaphragm Thickness (layers):", "Display Fill Edges:", "Display Intersecting Circles:", "Display Slice:", "Display Travel:", "Display:", "Extrusion Diameter (mm):", "Extrusion Density (ratio):", "Extrusion Width Over Thickness (ratio):", "Feedrate (mm/s):", "Fill:", "Fill Begin Rotation (degrees):", "Fill Density (ratio):", "Fill Odd Layer Extra Rotation (degrees):", "Infill Perimeter Overlap (ratio):", "Import Coarseness (ratio):", "Import:", "Import Tiny Details:", "Selection Export:", "Selection Print:", "Slices Beginning (layer):", "Slices Maximum Number (layers):", "Solid Surface Layers (integer):" }; // change the user interface parameters from default to preferences Vector widgetVector = new Vector(); RadioButtonGroup[] radioButtonGroups = new RadioButtonGroup[] { displayRadioButtonGroup, fillRadioButtonGroup, importRadioButtonGroup }; preferencesAddRadioButtonGroups( radioButtonGroups, widgetVector ); preferencesAddWidgetsWithStrings( widgets, widgetStrings, widgetVector ); preferencesRead( preferencesFilename, widgetVector ); dialog = new ComponentsDialog( window, "Forge the Selection", widgets, widgetStrings ); if ( !dialog.clickedOk() ) return; preferencesWrite( preferencesFilename, widgetVector ); global.diaphragmPeriod = ( int )diaphragmPeriodValueField.getValue(); global.diaphragmThickness = ( int )diaphragmThicknessValueField.getValue(); double extrusionDiameter = extrusionDiameterValueField.getValue(); double feedratePerMinute = Math.round( feedratePerSecondValueField.getValue() * 60.0 ); double fillBeginRotation = fillBeginRotationValueField.getValue() * Math.PI / 180.0; double fillDensity = fillDensityValueField.getValue(); double fillOddLayerExtraRotation = fillOddLayerExtraRotationValueField.getValue() * Math.PI / 180.0; double importCoarseness = importCoarsenessValueField.getValue(); double squareSectionWidth = extrusionDiameter * Math.sqrt( Math.PI / extrusionFillDensityValueField.getValue() ) / 2.0; double extrusionWidthOverThicknessSquareRoot = Math.sqrt( extrusionWidthOverThicknessValueField.getValue() ); double extrusionWidth = squareSectionWidth * extrusionWidthOverThicknessSquareRoot; double halfExtrusionWidth = extrusionWidth / 2; double layerThickness = squareSectionWidth / extrusionWidthOverThicknessSquareRoot; double absoluteFillPerimeterOverlap = halfExtrusionWidth * ( 1.0 - infillPerimeterOverlapValueField.getValue() ); int slicesBeginning = ( int )slicesBeginningValueField.getValue(); int slicesMaximumNumber = ( int )slicesMaximumNumberValueField.getValue(); global.solidSurfaceLayers = ( int )solidSurfaceLayersValueField.getValue(); global.doubleSolidSurfaceLayers = global.solidSurfaceLayers + global.solidSurfaceLayers; //double temperature = Math.round( temperatureValueField.getValue() ); curvesMeshesInfo = getCurvesMeshesInfo(); if ( curvesMeshesInfo == null ) { print( "There is no triangle mesh in the scene, so nothing will be done." ); print( "It may be that there is a solid shape which is not a triangle mesh, in which case convert that shape to a triangle mesh." ); return; } if ( selectionExportCheckbox.getState() ) { bufferedWriter = getBufferedWriter( curvesMeshesInfo[ 0 ].name ); } outputCurvesMeshes( curvesMeshesInfo );