/* abConvertPolygonToPath.ils Author A.D.Beckett Group Custom IC (UK), Cadence Design Systems Ltd. Language SKILL Date Aug 09, 2001 Modified By Code to attempt to convert polygons or rectangles to paths. Usage: abConvertPolygonToPath(obj) or abConvertPolygonToPath(list_of_objs) Typical usage: abConvertPolygonToPath(geGetSelSet()) The code is SKILL++ code - ensure that the .ils suffix is retained, to ensure it is interpreted correctly. This employs a fairly brute force algorithm. It uses rodCreatePath to create a path along the edge of one half of the polygon, starting at successive points in the polygon. For each path that was successfully created, it XORs the new shape with the original shape, in order to find out whether the path covers the original shape (and no more). If a matching path is found, the search stops. The algorithm is generally quite successful, and works in most cases where there is a solution - even with 45 degree paths. Non-45 degree paths are less likely to succeed. By default, the working layers are "y1", "y2", "y3". If you need to change these for whatever reason, this can be done as follows: abSetConvertPolygonWorkLayer("work1" '("annotate" "drawing1")) abSetConvertPolygonWorkLayer("work2" '("annotate" "drawing2")) abSetConvertPolygonWorkLayer("work3" '("annotate" "drawing3")) In other words, use the layers you need as the second argument. (Note to the interested. I don't use the create object from object feature of rodCreatePath - partly because that isn't available in 4.4.3, but also because it doesn't actually save that much code. I'd need to tell it the start and end handles to use - which would still need to be derived. Also, the width would still need to be calculated in the same way it's being done now). *************************************************** SCCS Info: @(#) abConvertPolygonToPath.ils 05/25/05.10:05:56 1.4 */ ;*;PUBLIC abConvertPolygonToPath ;*;PUBLIC abSetConvertPolygonWorkLayer /*************************************************************** * * * (abConvertPolygonToPath (obj)) * * * * The generic function which errors if any invalid arguments * * are given. * * * ***************************************************************/ (defgeneric abConvertPolygonToPath (obj) (error "abConvertPolygonToPath passed incorrect args %L\n" obj) ) /**************************************************************** * * * (abConvertPolygonToPath ((obj dbobject))) * * * * The method which handles converting database objects to paths * * * ****************************************************************/ (defmethod abConvertPolygonToPath ((obj dbobject)) (let ((cellView (getSGq obj cellView)) (origLayer (getSGq obj lpp)) (nPoints (getSGq obj nPoints)) (work1 (abGetConvertPolygonWorkLayer "work1")) (work2 (abGetConvertPolygonWorkLayer "work2")) (work3 (abGetConvertPolygonWorkLayer "work3")) (pointstried 0) newPoints width pt1 pt2 halfpoints found newShape hidewoport pointList xorResults ) ;----------------------------------------------------------------- ; Decide if this is a shape we can do anything with ;----------------------------------------------------------------- (cond ;---------------------------------------------------------------- ; Polygons - just get the point list ;---------------------------------------------------------------- ((and (equal (getSGq obj objType) "polygon") (evenp nPoints)) (setq pointList (getSGq obj points)) ) ;---------------------------------------------------------------- ; Rectangles - build the point list. Try to be vaguely ; smart by ordering the point list so that the algorithm ; below will choose the minimum width as the path width ;---------------------------------------------------------------- ((equal (getSGq obj objType) "rect") (let (ll ur w1 w2) (setq ll (lowerLeft (getSGq obj bBox))) (setq ur (upperRight (getSGq obj bBox))) (setq w1 (difference (xCoord ur) (xCoord ll))) (setq w2 (difference (yCoord ur) (yCoord ll))) (setq pointList (if (greaterp w1 w2) (list ll (list (xCoord ll) (yCoord ur)) ur (list (xCoord ur) (yCoord ll))) (list ll (list (xCoord ur) (yCoord ll)) ur (list (xCoord ll) (yCoord ur))) )) (setq nPoints 4) ) ) ) ;----------------------------------------------------------------- ; If a point list was found above, try to find a path ; which fits the polygon ;----------------------------------------------------------------- (when pointList (setq halfpoints (rightshift nPoints 1)) ;----------------------------------------------------------- ; Set up a port for hiding warnings. Unfortunately ; the rodCreatePath function creates two sets of warnings, ; and so getWarn can't swallow them ;----------------------------------------------------------- (setq hidewoport (outfile "/dev/null")) ;----------------------------------------------------------- ; Move the original object onto a work layer ;----------------------------------------------------------- (setSGq obj work1 lpp) (foreach map subList pointList (unless (or found (geqp pointstried halfpoints)) ;------------------------------------------ ; Get the guessed width - the length ; of the first segment of the subList ;------------------------------------------ (setq pt1 (car subList)) (setq pt2 (cadr subList)) (setq width (sqrt (plus (expt (difference (xCoord pt2) (xCoord pt1)) 2) (expt (difference (yCoord pt2) (yCoord pt1)) 2) ))) ;------------------------------------------ ; Build the new point list - this is ; the first "halfpoints" number of points ; in the rest of the subList ;------------------------------------------ (setq newPoints nil) (for point 1 halfpoints (setq subList (cdr subList)) (setq newPoints (cons (car subList) newPoints)) ) ;------------------------------------------ ; Try creating the path with both left and right ; justification (if necessary). Note that ; the errset is there to trap errors thrown ; if the point list was invalid. ;------------------------------------------ (forall just '("left" "right") (progn ;--------------------------------- ; Invoke wrapper around rodCreatePath ;--------------------------------- (setq newShape (abConvertPolygonToPathCreatePath hidewoport cellView work2 width newPoints just )) (getWarn) ;--------------------------------- ; If a shape was created, Xor the ; original shape with the new shapes. ;--------------------------------- (when newShape (setq xorResults (leLayerXor cellView work1 work2 work3) ) ;--------------------------- ; If there was anything output ; by the Xor, then the path isn't ; the same as the polygon, so ; delete this attempt ;--------------------------- (if xorResults (progn (foreach shape xorResults (dbDeleteObject shape ) ) (dbDeleteObject (getSGq newShape dbId)) ) ;----------------------- ; Otherwise we've found a ; matching path ;----------------------- (setq found t) ) ) (null found) ) ) ;------------------------------------------ ; Increment the number of points tried. There's ; no need to go past half way around the ; polygon ;------------------------------------------ (postincrement pointstried) ) ) ;----------------------------------------------------------- ; Close the port for hiding warnings ;----------------------------------------------------------- (when hidewoport (close hidewoport)) ) ;----------------------------------------------------------------- ; Cleanup. If it was found, delete the original object ; and move the new path onto the right layer. Otherwise make ; sure that the original shape is back on the right layer ;----------------------------------------------------------------- (if found (progn (setSGq (getSGq newShape dbId) origLayer lpp) (dbDeleteObject obj) (getSGq newShape dbId) ) (progn (setSGq obj origLayer lpp) nil ) ) ) /*************************************************************** * * * (abConvertPolygonToPath ((obj list))) * * * * A method which handles a list of objects being passed to the * * function. This does a mapcar on the entries in the list * * * ***************************************************************/ (defmethod abConvertPolygonToPath ((obj list)) (mapcar abConvertPolygonToPath obj) ) /***************************************************************************** * * * (abConvertPolygonToPathCreatePath woport cellView layer width points just) * * * * Function to wrap up creating the path (or at least trying) in order * * to trap any errors or warnings. Note that this is defined as * * a SKILL (i.e. dynamically scoped) function in order to allow * * woport to be safely overridden * * * *****************************************************************************/ (inSkill (defun abConvertPolygonToPathCreatePath (woport cellView layer width points just) (let (newShape) (errset (setq newShape (rodCreatePath ?cvId cellView ?layer layer ?width width ?pts points ?justification just ) ) ) ; errset newShape ) ; let ) ; defun ) ; inSkill /*************************************************************** * * * A lexically scoped bit of code to provide a means of hiding * * a "global" variable. This is to allow the user to override * * the working layers used. * * * ***************************************************************/ (let (workLayers) (setq abGetConvertPolygonWorkLayer (lambda (layerName) (arrayref workLayers layerName)) ) (setq abSetConvertPolygonWorkLayer (lambda (layerName actualLayer) (setarray workLayers layerName actualLayer)) ) ;---------------------------------------------------------------------- ; Initialise the workLayers table with the default working ; layers ;---------------------------------------------------------------------- (setq workLayers (makeTable 'abConvertPolygonWorkLayers nil)) (abSetConvertPolygonWorkLayer "work1" '("y1" "drawing")) (abSetConvertPolygonWorkLayer "work2" '("y2" "drawing")) (abSetConvertPolygonWorkLayer "work3" '("y3" "drawing")) )