0
mirror of https://github.com/uob-hep-cad/upenn-dune-gib.git synced 2026-09-01 23:01:12 +00:00
Files
upenn-dune-gib/allegro/logoMaker_public.il
2025-07-04 10:05:45 +01:00

1233 lines
62 KiB
Plaintext

;============================================================================
; Copyright (C) 2008 - 2017 Dave Elder. All rights reserved.
;
; Program Name: logoMaker_public.il
; Author: Dave Elder. dave.elder@gmail.com
;
; Purpose:
; Create graphics such as logos in Allegro based on bitmap-type images.
;
; When using Allegro Layout, scaled and/or mirrored graphics are attached
; to a mechanical symbol. This symbol may be subsequently moved.
;
; By running this utility in the Allegro symbol editor you can create a
; (non-scalable) symbol for placing in your company library.
;
; Limitations:
; potrace creates a Scalable Vector Graphics (SVG) file containing lines and
; bezier curves to define the outline of polygons. This Skill program draws
; the Bezier curves using a series of straight lines. In some instances this
; will result in Allegro shapes that do not represent the original image well.
; I will happily incorporate a circular arc fitting routine if a maths guru
; has time to develop one and sends it to me.
;
; Platforms tested (It should work on others, including Linux, also.):
; Windows 7
;
; Allegro verions:
; 16.6, 17.x
;
; Acknowledgements:
; This utility relies heavily on the potrace open source code generously
; provided by Peter Selinger.
; http://potrace.sourceforge.net
; It uses the open source ImageMagick Command Line Image File Converter
; available as a free download.
; http://www.imagemagick.org
;
;
; WARRANTY:
; None. This program is free software and is available as-is
; It may not work in all environments. There is no formal support.
;
; Modification and Distribution
; You may redistribute and/or modify this utility. However, in the open-source
; spirit in which this is provided to the Cadence community, any improvements
; and enhancements should be submitted to the originator for inclusion in future
; releases.
;
;============================================================================
;
; Installation instructions:
; Download and install the latest precompiled potrace release suitable for your platform:
; http://potrace.sourceforge.net/download
; set the POTRACE_PATH environment variable to the path to the executable
; eg. C:\utils\potrace
; If using Program Files folder (not advised) use [drive letter]:\Progra~1\potrace
;
; Download and install the latest precompiled ImageMagick release suitable for your platform
; This may not be necessary if you use Linux as ImageMagick is often included in Linux installations:
; http://www.imagemagick.org/script/binary-releases.php"
; eg. http://www.imagemagick.org/download/binaries/ImageMagick-6.5.3-Q16-windows.zip
; Set the MAGICK_HOME environment variable to the path to the convert executable.
; eg. C:\utils\ImageMagick\ImageMagick-6.5.1-3
;
; Install logoMaker_public.il into your skill folder.
; Add load("logoMaker_public.il") to the allegro.ilinit file in your pcbenv folder.
;
; Running within Allegro:
; Type logomaker in the command window.
;
;============================================================================
;
; History:
;
; 1.0 02-Jul-08 DE Original.
; 1.1 14-Jul-08 DE Added LFC_PATH. Requires DLL's to be in the same folder (as lfc.exe).
; 2.0 23-Jul-08 DE Discarded LFC in favour of ImageMagick. Multi-platform, open source and faster. (Thanks Charlie Davies!)
; 2.1 22-Apr-09 DE Added path name checking. Use convert from IMAGEMAGICK.
; 2.2 29-Apr-09 DE Remember last file type selected. Modify slashes for Unix system use. Hide form while placing graphics.
; 2.3 02-Jun-09 DE Added advanced options as optional. Define functions for axlSetDynamicsMirror and axlSetDynamicsRotation if unavailable.
; Embedded help text.
; 2.4 02-Jun-09 DE Added general info button.
; 2.5 03-Jun-09 DE Invoke Grayscale option for Imagemagick - solves 32bit GIF image conversion problems.
; 2.6 04-Jun-09 DE Added +extent to ImageMagick conversion. Seems to help with some GIF images. (Thanks Evan Shultz)
; 2.7 23-Jun-09 DE Set standard image size (2400x2400) for Imagemagick conversion to ensure form image is displayed well.
; 2.8 24-Jun-09 DE Rearranged form to put Advanced options button at top.
; 2.9 21-Aug-09 DE Added "Update thumbnail" option to improve performance when user is changing several pre-processing options. (Thanks Craig Lewis)
; 3.0 02-Jul-10 DE Allow ImageMagick to be loaded in a folder containing a space eg. C:\Program Files.
; Use shapes instead of symbol on cursor if not a layout drawing.
; 3.1 02-Jul-10 DE Image name and path may now include spaces.
; 3.2 16-Dec-10 DE Activate "potracedefaults" button action that had been omitted.
; 3.3 08-Jun-11 DE Update for 16.5. Logo bmp on form was not updating because temp file name was always the same.
; 3.4 18-Aug-11 DE Update for 16.5 to replace use of drafting symbol type with format.
; 3.7 06-Aug-14 DE Update to allow for recent versions of ImageMagick (added "BMP2:" to convert command")
; 4.0 01-Aug-17 DE Improved missing plugins error messaging. Use embedded form (16.6+ only). Added magick.exe for using later versions of ImageMagick. Thanks Matt Garnett!
;
;============================================================================
;
axlCmdRegister("logomaker" 'logoMaker_start); Must be interactive (the default).
defun( logoMaker_start ()
prog((potracePath, magickHome, convertEXE, designUnits, y, fStr, class, subclass, form)
potracePath = getShellEnvVar("POTRACE_PATH")
magickHome = getShellEnvVar("MAGICK_HOME")
convertEXE = cond(
(!magickHome && zerop(system("convert -version")), "convert")
(!magickHome, nil)
(axlVersion('isWindows) && isFile(strcat(magickHome, "/convert.exe")), strcat(magickHome, "/convert.exe"))
(axlVersion('isWindows) && isFile(strcat(magickHome, "/magick.exe")), strcat(magickHome, "/magick.exe")); For later versions of ImageMagick that don't include convert.exe. DE 01-Aug-17
(!axlVersion('isWindows) && isFile(strcat(magickHome, "/convert")), strcat(magickHome, "/convert"))
)
unless(logoMaker_check4plugins(?potracePath potracePath, ?convertEXE convertEXE), return())
designUnits = car(axlDBGetDesignUnits()), when(designUnits == "millimeters", designUnits = "mm")
y = 1
fStr = logoMaker_formStart(80, "Dave Elder's Logo Maker Utility v4.0")
fStr = logoMaker_formMessage(fStr, "infomsg", ?level 0, ?msgString "%s")
fStr = logoMaker_formMessage(fStr, "warnmsg", ?level 2, ?msgString "%s")
fStr = logoMaker_formMessage(fStr, "errormsg", ?level 3, ?msgString "%s")
fStr = logoMaker_formPopupDefine(fStr, "TURNPOLICY", '("black", "white", "right", "left", "minority", "majority", "random"))
fStr = logoMaker_formPopupDefine(fStr, "ROTATION", '(0 45 90 135 180 225 270 315))
fStr = logoMaker_formPopupDefine(fStr, "CLASS")
fStr = logoMaker_formPopupDefine(fStr, "SUBCLASS")
fStr = logoMaker_formStrFillin(fStr, "imagefile", 62, 199, 1, y, ?title "Image file:", ?x2 10, ?options "dropfile")
fStr = logoMaker_formButton(fStr, "imagebrowse", "...", 75, y, 4, 3), y = y + 2
fStr = logoMaker_formChecklistItem(fStr, "viewadvancedoptions", 2, y + 1, "View advanced options")
y = y + 1
fStr = logoMaker_formThumbnail(fStr, "pbmthumbnail_small", 46, y, 33, 21, ?options "stretch")
fStr = logoMaker_formThumbnail(fStr, "pbmthumbnail_big", 17, y + 2, 62, 38, ?options "stretch")
y = y + 2
fStr = logoMaker_formGroup(fStr, "Pre-processing Settings", 1, y, 43, 19 ?label "mkbitmapgrp"), y = y + 2
fStr = logoMaker_formChecklistItem(fStr, "mkbitmap", 2, y, "Pre-process")
fStr = logoMaker_formChecklistItem(fStr, "mkbitmapupdate", 15, y, "Update thumbnail")
fStr = logoMaker_formButton(fStr, "mkbitmapdefaults", "Defaults", 35, y, 6, 3), y = y + 2
fStr = logoMaker_formText(fStr, "Pre-processes colour/greyscale image for improved tracing.", 2, y, ?label "mkbitmapinfo"), y = y + 2
fStr = logoMaker_formChecklistItem(fStr, "mkbitmapinvert", 3, y, "Invert the input (undo 'blackboard' effect)"), y = y + 2
fStr = logoMaker_formChecklistItem(fStr, "mkbitmaphipassfilter", 3, y, "Apply High Pass Filtering")
fStr = logoMaker_formText(fStr, "No Image Selected.", 54, y, ?label "noimage_small", ?options "CENTER")
fStr = logoMaker_formIntSlideBar(fStr, "mkbitmapfilterradius", 2, 2, 26, y, 0, 99, ?title "Radius:", ?fillInX 33, ?value 4), y = y + 2
fStr = logoMaker_formIntSlideBar(fStr, "mkbitmapscale", 2, 2, 3, y, 1, 99, ?title "Pre-scale:", ?fillInX 10, ?value 2), y = y + 2
fStr = logoMaker_formChecklistItem(fStr, "mkbitmapcubic", 3, y, "Use cubic interpolation", ?radioLabel "mkbitmapinterp")
fStr = logoMaker_formChecklistItem(fStr, "mkbitmaplinear", 23, y, "Use linear interpolation", ?radioLabel "mkbitmapinterp"), y = y + 2
fStr = logoMaker_formChecklistItem(fStr, "mkbitmapbilevel", 3, y, "Bilevel conversion", ?radioLabel "mkbitmapconversion")
fStr = logoMaker_formChecklistItem(fStr, "mkbitmapgrey", 23, y, "Greymap conversion", ?radioLabel "mkbitmapconversion"), y = y + 2
fStr = logoMaker_formRealFillin(fStr, "mkbitmapthreshold", 4, 4, 3, y, ?title "Bilevel Conversion Threshold:", ?fillInX 24, ?value 0.45)
fStr = logoMaker_formText(fStr, "No Image Selected.", 28, y, ?label "noimage_big", ?options "CENTER")
fStr = logoMaker_formButton(fStr, "mkbitmaphelp", "Info", 37, y + 1, 4, 2), y = y + 3
fStr = logoMaker_formGroup(fStr, "Tracing Options", 1, y, 78, 19 ?label "potracegrp"), y = y + 2
;Algorithm options:
fStr = logoMaker_formStrFillin(fStr, "turnpolicy", 6, 8, 3, y, ?pop "TURNPOLICY", ?title "Turnpolicy:", ?x2 13)
fStr = logoMaker_formText(fStr, "- How to resolve ambiguities in path decomposition", 26, y, ?label "turnpolicy_note")
fStr = logoMaker_formButton(fStr, "potracedefaults", "Defaults", 70, y, 6, 3), y = y + 2
fStr = logoMaker_formIntSlideBar(fStr, "turdsize", 3, 3, 3, y, 1, 999, ?title "Speckle Size:", ?fillInX 13, ?value 2, ?units "pixels")
fStr = logoMaker_formText(fStr, "- Suppress speckles of up to this size", 26, y, ?label "turdsize_note"), y = y + 2
fStr = logoMaker_formRealFillin(fStr, "alphamax", 5, 5, 3, y, ?title "Corner Threshold:", ?fillInX 16, ?minVal -1, ?maxVal 1.334, ?value 1.0)
fStr = logoMaker_formText(fStr, "- The smaller this value, the more sharp corners will be produced.", 26, y, ?label "alphamax_note"), y = y + 2
fStr = logoMaker_formChecklistItem(fStr, "longcurve", 3, y, "Curve Optimisation")
fStr = logoMaker_formText(fStr, "- Join adjacent Bezier curve segments when possible.", 26, y, ?label "longcurve_note"), y = y + 2
fStr = logoMaker_formRealFillin(fStr, "opttolerance", 4, 4, 3, y, ?title "Tolerance:", ?fillInX 12, ?minVal 0, ?maxVal 1.0, ?value 0.2)
fStr = logoMaker_formText(fStr, "- Curve optimisation tolerance.", 26, y, ?label "opttolerance_note"), y = y + 2
fStr = logoMaker_formIntSlideBar(fStr, "unitn", 2, 2, 3, y, 1, 99, ?title "Output Quantisation:", ?fillInX 18, ?value 10)
fStr = logoMaker_formText(fStr, "- Quantise output to 1/unit pixels.", 26, y, ?label "unitn_note"), y = y + 2
;Frontend options:
fStr = logoMaker_formRealFillin(fStr, "blacklevel", 4, 4, 3, y, ?title "Black Level:", ?fillInX 13, ?minVal 0, ?maxVal 1.0, ?value 0.5)
fStr = logoMaker_formText(fStr, "- Black/white cutoff in input file.", 26, y, ?label "blacklevel_note"), y = y + 2
fStr = logoMaker_formChecklistItem(fStr, "inverted", 3, y, "Inverted")
fStr = logoMaker_formText(fStr, "- Invert the input bitmap before processing.", 26, y, ?label "inverted_note")
fStr = logoMaker_formButton(fStr, "potracehelp", "Info", 72, y + 1, 4, 2), y = y + 4
fStr = logoMaker_formRealFillin(fStr, "width", 5, 5, 2, y, ?title "Width:", ?fillInX 7, ?units designUnits)
fStr = logoMaker_formRealFillin(fStr, "height", 5, 5, 22, y, ?title "Height:", ?fillInX 27, ?units designUnits)
fStr = logoMaker_formChecklistItem(fStr, "aspectratio", 42, y, "Maintain Aspect Ratio")
fStr = logoMaker_formButton(fStr, "defaultsizes", "Default Size", 65, y, 12, 3), y = y + 3
fStr = logoMaker_formIntegerFillin(fStr, "rotation", 3, 4, 2, y, ?pop "ROTATION", ?title "Rotation:", ?fillInX 9, ?minVal -359, ?maxVal 359, ?value 0, ?units "degrees")
fStr = logoMaker_formText(fStr, "(Anticlockwise)", 25, y)
fStr = logoMaker_formChecklistItem(fStr, "mirror", 42, y, "Mirror")
fStr = logoMaker_formChecklistItem(fStr, "fixed", 52, y, "Fix Symbol"), y = y + 2
fStr = logoMaker_formText(fStr, "Class/Subclass for Graphics:", 2, y), y = y + 2
fStr = logoMaker_formPop(fStr, "class", "CLASS", 5, y, 19, ?options "prettyprint"), y = y + 2
fStr = logoMaker_formColor(fStr, "subcolour", 2, y, 2, 1)
fStr = logoMaker_formPop(fStr, "subclass", "SUBCLASS", 5, y, 19 ?options "prettyprint ownerdrawn")
fStr = logoMaker_formStrFillin(fStr, "symbolname", 27, 27, 30, y, ?title "Symbol Name:", ?x2 41), y = y + 3
fStr = logoMaker_formButton(fStr, "createlogo", "Place Logo", 30, y, 20, 4), y = y + 1
fStr = logoMaker_formButton(fStr, "generalinfo", "Info", 60, y, 8, 3)
fStr = logoMaker_formButton(fStr, "cancel", "Exit", 70, y, 8, 3)
fStr = logoMaker_formEnd(fStr)
form = logoMaker_formCreateEmbedded(fStr, 'logoMaker_formAction)
if(form then
form ->potracePath = potracePath
form ->convertEXE = convertEXE
form ->tempFiles = nil
form ->designUnits = designUnits
axlFormColorize(form, "mkbitmapinfo", 'text, 'blue)
logoMaker_mkBitmapDefaults(form)
logoMaker_potraceDefaults(form)
logoMaker_setOptionsLevelVisibility(form)
foreach(field, '("width", "height", "defaultsizes", "createlogo"), axlFormSetFieldEditable(form, field, nil))
axlFormSetField(form, "aspectratio", t)
class = axlMapClassName("PACKAGE GEOMETRY"); Must be upper case
subclass = "SILKSCREEN_TOP"
axlBuildClassPopup(form, "class")
axlFormSetField(form, "class", class)
axlBuildSubclassPopup(form, "subclass", class)
axlFormSetField(form, "subclass", subclass)
logoMaker_formSetLayerSubColour(form, "subcolour", strcat(class, "/", subclass))
logoMaker_check4plugins(?form form, ?potracePath form ->potracePath, ?convertEXE form ->convertEXE)
axlFormDisplay(form)
else
axlUIConfirm("Error: Could not create the form")
)
))
defun( logoMaker_mkBitmapDefaults (form)
logoMaker_needMkbitmap(form)
axlFormSetField(form, "mkbitmapinvert", nil); Invert the input (undo 'blackboard' effect).
axlFormSetField(form, "mkbitmaphipassfilter", t); Apply highpass filter with radius n.
axlFormSetField(form, "mkbitmapfilterradius", 4)
axlFormSetFieldEditable(form, "mkbitmapfilterradius", t)
axlFormSetField(form, "mkbitmapscale", 2); Scale by integer factor n.
axlFormSetField(form, "mkbitmapcubic", t); Use cubic interpolation (else use linear interpolation).
axlFormSetField(form, "mkbitmapbilevel", t); Enable bilevel conversion (or output a greymap).
axlFormSetField(form, "mkbitmapthreshold", 0.45); Set threshold for bilevel conversion.
axlFormSetFieldEditable(form, "mkbitmapthreshold", t)
)
defun( logoMaker_setOptionsLevelVisibility (form)
let((smallView, vis, fields)
smallView = axlFormGetField(form, "viewadvancedoptions")
vis = if(smallView, 1, 0)
fields = '("grp", "update", "defaults", "info", "invert", "hipassfilter", "filterradius", "filterradius_title"
"scale", "scale_title", "cubic", "linear", "bilevel", "grey", "threshold", "threshold_title", "help")
foreach(field, fields, axlFormSetFieldVisible(form, strcat("mkbitmap", field), vis))
fields = '("potracegrp", "potracedefaults", "turnpolicy", "turnpolicy_title", "turnpolicy_note", "turdsize", "turdsizeUnits", "turdsize_title", "turdsize_note"
"alphamax", "alphamax_title", "alphamax_note", "longcurve", "longcurve_note", "opttolerance", "opttolerance_title", "opttolerance_note"
"unitn", "unitn_title", "unitn_note", "blacklevel", "blacklevel_title", "blacklevel_note", "inverted_note", "potracehelp"
)
foreach(field, fields, axlFormSetFieldVisible(form, field, vis))
vis = form ->bmpfile
axlFormSetFieldVisible(form, "pbmthumbnail_small", if(smallView && vis, 1, 0))
axlFormSetFieldVisible(form, "noimage_small", if(smallView && !vis, 1, 0))
axlFormSetFieldVisible(form, "pbmthumbnail_big", if(!smallView && vis, 1, 0))
axlFormSetFieldVisible(form, "noimage_big", if(!smallView && !vis, 1, 0))
))
defun( logoMaker_checkImageFile (form, imageFile)
let((bmpFileName, fileInfo, pathName, fileName, extn, msg, cwd, cmd)
form ->bmpfile = nil
if(imageFile && isFile(imageFile) then
fileInfo = axlDMFileParts(imageFile)
pathName = car(fileInfo)
fileName = cadr(fileInfo)
extn = cadddr(fileInfo)
when(extn, extn = lowerCase(extn))
form ->extn = extn
cond(
(pathName && !logoMaker_validPathName(pathName)
sprintf(msg, "Path name contains invalid characters (%s)", logoMaker_validPathName(pathName, t))
)
(!logoMaker_validFileName(fileName)
sprintf(msg, "File name contains invalid characters (%s)", logoMaker_validFileName(fileName, t))
)
(t
if(extn == "bmp" then
bmpFileName = imageFile
else
cwd = getWorkingDir()
sprintf(bmpFileName, "%s/%s.bmp", cwd, fileName)
sprintf(cmd, "%s \"%s\" -format bmp -strip -resize \"2400x2400\" -colors 256 \"%s\"", form ->convertEXE, imageFile, bmpFileName)
axlRunBatchDBProgram("convert", cmd, ?noUnload t, ?startMsg "Converting to bmp", ?silent t, ?noProgress t)
)
axlFormSetField(form, "imagefile", imageFile)
form ->bmpfile = bmpFileName
if(bmpFileName then
logoMaker_needMkbitmap(form); sets/unsets the pre-processing radio button
if(logoMaker_drawPbm(form) then
rexCompile(" "), axlFormSetField(form, "symbolname", rexReplace(fileName, "_", 0))
unless(extn == "bmp", form ->tempFiles = unique(cons(bmpFileName, form ->tempFiles)))
else form ->bmpfile = bmpFileName = nil
)
else msg = "Not a known or valid raster graphics image"
)
)
); cond
else
when(imageFile && imageFile != "", sprintf(msg, "File (%s) does not exist", imageFile))
); if
when(msg, axlFormMsg(form, "warnmsg", msg))
logoMaker_setSizeFields(form)
unless(bmpFileName
axlFormSetField(form, "imagefile", nil)
axlFormSetField(form, "pbmthumbnail_small", nil)
axlFormSetField(form, "pbmthumbnail_big", nil)
axlFormSetFieldVisible(form, "noimage_small", if(axlFormGetField(form, "viewadvancedoptions"), 1, 0))
axlFormSetFieldVisible(form, "noimage_big", if(axlFormGetField(form, "viewadvancedoptions"), 0, 1))
axlFormSetFieldVisible(form, "pbmthumbnail_small", 0)
axlFormSetFieldVisible(form, "pbmthumbnail_big", 0)
axlFormSetField(form, "symbolname", nil)
)
))
defun( logoMaker_needMkbitmap (form)
let((bmpFileName, p, nBits, (nColours 999))
bmpFileName = form ->bmpfile
when(bmpFileName && isFile(bmpFileName)
p = infile(bmpFileName)
for(i, 1, 28, getc(p))
;Bits 29 & 30 give the biBitCount:
nBits = charToInt(getc(p)) + 256 * charToInt(getc(p))
for(i, 1, 16, getc(p))
;Bits 47 - 50 give the biClrUsed:
nColours = charToInt(getc(p)) + 256 * charToInt(getc(p)) + 256**2 * charToInt(getc(p)) + 256**3 * charToInt(getc(p))
close(p)
)
axlFormSetField(form, "mkbitmap", nColours > 0 || nBits > 1)
))
defun( logoMaker_potraceDefaults (form)
axlFormSetField(form, "turnpolicy", "minority"); How to resolve ambiguities in path decomposition.
axlFormSetField(form, "turdsize", 2); Suppress speckles of up to this size.
axlFormSetField(form, "alphamax", 1.0); Corner threshold parameter.
axlFormSetField(form, "longcurve", t); Turn on curve optimization.
axlFormSetField(form, "opttolerance", 0.2); Curve optimization tolerance.
axlFormSetField(form, "unitn", 10); Quantize output to 1/unit pixels.
axlFormSetField(form, "blacklevel", 0.5); Black/white cutoff in input file.
axlFormSetField(form, "inverted", nil); Invert bitmap off.
)
defun( logoMaker_check4plugins (@key form, potracePath, convertEXE)
let((converterOK, cmd, mkbitmapOK, potraceOK, missingPlugins, msg)
converterOK = and(convertEXE, isExecutable(convertEXE))
when(potracePath
sprintf(cmd, "%s/mkbitmap -v", potracePath)
mkbitmapOK = !numberp(axlRunBatchDBProgram("makebitmap", cmd, ?noUnload t, ?silent t, ?noProgress t))
sprintf(cmd, "%s/potrace -v", potracePath)
potraceOK = !numberp(axlRunBatchDBProgram("potrace", cmd, ?noUnload t, ?silent t, ?noProgress t))
)
missingPlugins = remq(t, list(or(converterOK, "ImageMagick convert"), or(mkbitmapOK, "makebitmap"), or(potraceOK, "potrace")))
when(missingPlugins
sprintf(msg, "Missing plugins: %s. Contact your administrator", buildString(missingPlugins, ", "))
if(form then
foreach(field, '("imagefile", "imagebrowse", "createlogo"), axlFormSetFieldEditable(form, field, 0))
axlFormMsg(form, "errormsg", msg)
else
println(msg)
)
logoMaker_Help("Plugins")
)
!missingPlugins
))
defun( logoMaker_formAction (form)
let((value, mkBitmap, mkBitmapUpdate, filters, extn, imageFile, msg, class, subclass, layer, subclasses)
value = form ->curValue
mkBitmap = axlFormGetField(form, "mkbitmap")
mkBitmapUpdate = axlFormGetField(form, "mkbitmapupdate")
case(form ->curField
("imagebrowse"
filters = '(("bmp", "Bitmap")("jpg", "Jpeg")("gif", "GIF"))
and(
extn = form ->extn
extn != caar(filters)
extn = assoc(extn, filters)
filters = cons(extn, remove(extn, filters)); Make the previously selected image type the default.
)
filters = mapcar(lambda((f), sprintf(nil, "%s files (*.%s)|*.%s" car(f), cadr(f), cadr(f))), mapcar('reverse, filters))
imageFile = axlDMFileBrowse(nil, nil, ?title "Select Image file", ?optFilters buildString(filters, "|"))
logoMaker_checkImageFile(form, imageFile)
)
("imagefile", logoMaker_checkImageFile(form, value))
("mkbitmap", logoMaker_drawPbm(form))
("mkbitmapupdate", when(mkBitmap && mkBitmapUpdate, logoMaker_drawPbm(form)))
("mkbitmapdefaults", logoMaker_mkBitmapDefaults(form), when(axlFormGetField(form, "mkbitmap") && mkBitmapUpdate, logoMaker_drawPbm(form)))
("mkbitmapinvert", when(mkBitmap && mkBitmapUpdate, logoMaker_drawPbm(form)))
("mkbitmaphipassfilter", axlFormSetFieldEditable(form, "mkbitmapfilterradius", value), when(mkBitmap && mkBitmapUpdate, logoMaker_drawPbm(form)))
("mkbitmapfilterradius", when(mkBitmap && mkBitmapUpdate, logoMaker_drawPbm(form)))
("mkbitmapscale", when(mkBitmap && mkBitmapUpdate, logoMaker_drawPbm(form)))
("mkbitmapcubic", when(mkBitmap && mkBitmapUpdate, logoMaker_drawPbm(form)))
("mkbitmapbilevel", axlFormSetFieldEditable(form, "mkbitmapthreshold", value), when(mkBitmap && mkBitmapUpdate, logoMaker_drawPbm(form)))
("mkbitmapthreshold", when(mkBitmap && mkBitmapUpdate, logoMaker_drawPbm(form)))
("mkbitmaphelp", logoMaker_Help("Pre-processing Options"))
("potracedefaults", logoMaker_potraceDefaults(form))
("potracehelp", logoMaker_Help("Tracing Options"))
("width"
when(axlFormGetField(form, "aspectratio")
axlFormRestoreField(form, "width")
axlFormSetField(form, "height", value / axlFormGetField(form, "width") * axlFormGetField(form, "height"))
axlFormSetField(form, "width", value)
)
)
("height"
when(axlFormGetField(form, "aspectratio")
axlFormRestoreField(form, "height")
axlFormSetField(form, "width", value / axlFormGetField(form, "height") * axlFormGetField(form, "width"))
axlFormSetField(form, "height", value)
)
)
("defaultsizes", logoMaker_setSizeFields(form))
("rotation", logoMaker_setDynamicsRotMir(form))
("mirror", logoMaker_setDynamicsRotMir(form))
("class"
class = value, subclass = axlFormGetField(form, "subclass")
layer = strcat(class, "/" subclass)
axlBuildSubclassPopup(form, "subclass", class)
unless(axlIsLayer(layer)
subclasses = axlGetParam(strcat("paramLayerGroup:", class)) ->groupMembers
subclass = car(setof(sub, subclasses, axlIsVisibleLayer(strcat(class, "/", sub))) || subclasses)
)
axlFormSetField(form, "subclass", subclass)
logoMaker_formSetLayerSubColour(form, "subcolour", strcat(class, "/" subclass))
logoMaker_setDynamicsRotMir(form)
)
("subclass"
class = axlFormGetField(form, "class"), subclass = value
logoMaker_formSetLayerSubColour(form, "subcolour", strcat(class, "/" subclass))
logoMaker_setDynamicsRotMir(form)
)
("subcolour"
class = axlFormGetField(form, "class")
when(class
subclass = axlFormGetField(form, "subclass")
layer = strcat(class, "/" subclass)
when(axlIsLayer(layer), axlVisibleLayer(layer, !axlIsVisibleLayer(layer)), axlVisibleUpdate(t))
logoMaker_formSetLayerSubColour(form, "subcolour", layer)
)
)
("symbolname"
unless(logoMaker_validSymbolName(value)
axlFormRestoreField(form, "symbolname")
sprintf(msg, "'%s' contains invalid symbol name characters (%s)", value, logoMaker_validSymbolName(value, t))
axlFormMsg(form, "warnmsg", msg)
)
)
("viewadvancedoptions", logoMaker_setOptionsLevelVisibility(form))
("createlogo"
axlUIWShow(form, 'hide)
logoMaker_create(form)
axlUIWShow(form, 'show)
)
("generalinfo", logoMaker_Help("About This Utility"))
("cancel"
foreach(file, form ->tempFiles, when(isFile(file), deleteFile(file)))
axlFormClose(form), axlCancelEnterFun()
)
)
))
defun( logoMaker_validSymbolName (name @optional returnBadCharacters), logoMaker_validName(name, "^[-a-zA-Z0-9_]+", returnBadCharacters))
defun( logoMaker_validFileName (name @optional returnBadCharacters), logoMaker_validName(name, "^[-a-zA-Z0-9_ ]+", returnBadCharacters))
defun( logoMaker_validPathName (name @optional returnBadCharacters), logoMaker_validName(name, "^[-.a-zA-Z0-9_:/\\ ]+", returnBadCharacters))
defun( logoMaker_validName (name, matchStr @optional returnBadCharacters)
let((chars, result)
chars = parseString(name, "")
result = forall(ch, chars, rexMatchp(matchStr, ch))
when(returnBadCharacters && !result
foreach(ch, chars, unless(rexMatchp(matchStr, ch), result = cons(ch, result)))
result = buildString(mapcar(lambda((ch), sprintf(nil, "'%s'", ch)), unique(result)), ", ")
)
result
))
defun( logoMaker_drawPbm (form); Converts the pbm to a bmp using ImageMagick utility before displaying it on the form.
let((bmpFileName, pbmFileName, mk_bmpFileName, cmd, fileParts, tmpFileName, thumbnailExists)
bmpFileName = form ->bmpfile
when(bmpFileName && isFile(bmpFileName)
if(axlFormGetField(form, "mkbitmap") then
pbmFileName = logoMaker_mkbitmap(form, bmpFileName)
if(pbmFileName then
fileParts = axlDMFileParts(pbmFileName)
sprintf(mk_bmpFileName, "%s/%s.bmp", car(fileParts), cadr(fileParts))
sprintf(tmpFileName, "%s_tmp.bmp", cadr(fileParts))
sprintf(cmd, "%s \"%s\" -format bmp -strip -resize \"2400x2400\" \"BMP2:%s\"", form ->convertEXE, pbmFileName, mk_bmpFileName)
axlRunBatchDBProgram("convert", cmd, ?noUnload t, ?startMsg "Converting to suitable bmp for form display", ?silent t, ?noProgress t)
if(isFile(mk_bmpFileName) then
form ->tempFiles = unique(cons(mk_bmpFileName, form ->tempFiles))
sprintf(cmd, "%s \"%s\" -strip -resize \"1200x1200\" -extent \"1200x1200\" BMP2:%s", form ->convertEXE, pbmFileName, tmpFileName)
axlRunBatchDBProgram("convert", cmd, ?noUnload t, ?startMsg "Converting to suitable bmp for form display", ?silent t, ?noProgress t)
if(isFile(tmpFileName) then
axlFormSetField(form, "pbmthumbnail_small", tmpFileName)
axlFormSetField(form, "pbmthumbnail_big", tmpFileName)
form ->tempFiles = unique(cons(tmpFileName, form ->tempFiles))
else
logoMaker_Help("ImageMagick bmp conversion failure")
axlFormMsg(form, "warnmsg", "ImageMagick conversion, from mkbitmap pbm file to bitmap for form display, failed")
)
else
logoMaker_Help("ImageMagick bmp conversion failure")
axlFormMsg(form, "warnmsg", "ImageMagick conversion, from mkbitmap pbm file to intermediate bitmap for form display, failed")
); if
else
logoMaker_Help("mkbitmap failure")
axlFormMsg(form, "warnmsg", "mkbitmap command failed")
)
else
fileParts = axlDMFileParts(bmpFileName)
sprintf(tmpFileName, "%s_tmp.bmp", cadr(fileParts))
sprintf(cmd, "%s \"%s\" -strip -resize \"1200x1200\" -extent \"1200x1200\" BMP2:%s", form ->convertEXE, bmpFileName, tmpFileName)
axlRunBatchDBProgram("convert", cmd, ?noUnload t, ?startMsg "Converting to suitable bmp for form display", ?silent t, ?noProgress t)
if(isFile(tmpFileName) then
axlFormSetField(form, "pbmthumbnail_small", tmpFileName)
axlFormSetField(form, "pbmthumbnail_big", tmpFileName)
form ->tempFiles = unique(cons(tmpFileName, form ->tempFiles))
else
logoMaker_Help("ImageMagick bmp conversion failure")
axlFormMsg(form, "warnmsg", "ImageMagick conversion, from original bitmap file to bitmap for form display, failed")
)
pbmFileName = bmpFileName
)
); when
thumbnailExists = isFile(axlFormGetField(form, "pbmthumbnail_small"))
axlFormSetFieldVisible(form, "noimage_small", if(thumbnailExists || !axlFormGetField(form, "viewadvancedoptions"), 0, 1))
axlFormSetFieldVisible(form, "pbmthumbnail_small", if(thumbnailExists && axlFormGetField(form, "viewadvancedoptions"), 1, 0))
thumbnailExists = isFile(axlFormGetField(form, "pbmthumbnail_big"))
axlFormSetFieldVisible(form, "noimage_big", if(thumbnailExists || axlFormGetField(form, "viewadvancedoptions"), 0, 1))
axlFormSetFieldVisible(form, "pbmthumbnail_big", if(thumbnailExists && !axlFormGetField(form, "viewadvancedoptions"), 1, 0))
pbmFileName
))
defun( logoMaker_setSizeFields (form)
let((bmpFileName, width, height, p, xPixels, yPixels)
bmpFileName = form ->bmpfile
when(bmpFileName && isFile(bmpFileName)
width = height = if(member(form ->designUnits, '("mils", "inches")), axlMKSConvert(1 "inches"), axlMKSConvert(25 "mm"))
p = infile(bmpFileName)
for(i, 1, 18, getc(p))
xPixels = charToInt(getc(p)) + 256 * charToInt(getc(p)) + 256**2 * charToInt(getc(p)) + 256**3 * charToInt(getc(p))
yPixels = charToInt(getc(p)) + 256 * charToInt(getc(p)) + 256**2 * charToInt(getc(p)) + 256**3 * charToInt(getc(p))
close(p)
if(xPixels >= yPixels, then height = yPixels / float(xPixels) * width, else width = xPixels / float(yPixels) * height)
)
axlFormSetField(form, "width", width), axlFormSetField(form, "height", height)
foreach(field, '("width", "height", "defaultsizes", "createlogo"), axlFormSetFieldEditable(form, field, width))
))
defun( logoMaker_setDynamicsRotMir (form)
let((rotation, mirror)
rotation = axlFormGetField(form, "rotation")
mirror = axlFormGetField(form, "mirror")
when(substring(axlFormGetField(form, "subclass"), -6) == "BOTTOM", mirror = !mirror)
axlSetDynamicsRotation(rotation)
axlSetDynamicsMirror(mirror)
))
defun( logoMaker_mkbitmap (form, bmpFile)
let((invert, hipassfilter, scale, interpolation, threshold, bitmapOutFile, options, cmd)
invert = if(axlFormGetField(form, "mkbitmapinvert"), "-i ", "")
if(axlFormGetField(form, "mkbitmaphipassfilter")
then sprintf(hipassfilter, "-f %d", axlFormGetField(form, "mkbitmapfilterradius"))
else sprintf(hipassfilter, "-n")
)
sprintf(scale, "-s %d", axlFormGetField(form, "mkbitmapscale"))
sprintf(interpolation, "-%d", if(axlFormGetField(form, "mkbitmapcubic"), 3, 1))
if(axlFormGetField(form, "mkbitmapbilevel")
then sprintf(threshold, "-t %2.2f", axlFormGetField(form, "mkbitmapthreshold"))
else sprintf(threshold, "-g")
)
sprintf(bitmapOutFile, "%s/%s_mk.pbm", getWorkingDir(), cadr(axlDMFileParts(bmpFile)))
sprintf(options "%s%s %s %s %s", invert, hipassfilter, scale, interpolation, threshold)
sprintf(cmd, "%s/mkbitmap %s \"%s\" -o \"%s\"", form ->potracePath, options, bmpFile, bitmapOutFile)
and(
!numberp(axlRunBatchDBProgram("makebitmap", cmd, ?noUnload t, ?silent t, ?noProgress t))
form ->tempFiles = unique(cons(bitmapOutFile, form ->tempFiles))
bitmapOutFile
)
))
defun( logoMaker_create (form)
let((bitmapFile, symbolName, tad, x, y, symDef, shapes, mark, popup, popupGray (continue t), event, pt, rot, mirror, sym, bmpFile)
bitmapFile = form ->bmpfile
when(bitmapFile && isFile(bitmapFile)
symbolName = axlFormGetField(form, "symbolname")
when(!symbolName || symbolName == "", symbolName = "bitmaplogo")
when(axlFormGetField(form, "mkbitmap")
sprintf(bitmapFile, "%s/%s_mk.bmp", getWorkingDir(), cadr(axlDMFileParts(bitmapFile)))
)
)
when(bitmapFile && isFile(bitmapFile)
tad = axlMKSConvert(1, "mils")
x = axlFormGetField(form, "width") + tad, y = axlFormGetField(form, "height") + tad
when(axlDesignType(nil) == "LAYOUT"
symDef = axlDBCreateSymDefSkeleton('("TEMPLOGO", "format"), list(-tad:-tad, x:y))
)
shapes = logoMaker_createSym(form, bitmapFile, ?symbol symDef)
axlSetDynamicsRotation(0)
axlSetDynamicsMirror(nil)
axlDynamicsObject(symDef || shapes, 0:0)
axlDeleteObject(symDef)
mark = axlDBTransactionStart()
form ->oopsCount = 0
popup = axlUIPopupDefine(nil '(
("Done", "axlDBTransactionCommit(mark), axlFinishEnterFun(), continue = nil")
("Oops", "axlDBTransactionOops(mark), when(zerop(form ->oopsCount = form ->oopsCount - 1), axlUIPopupSet(popupGray))")
("Cancel", "axlDBTransactionRollback(mark), axlCancelEnterFun(), continue = nil")
("MENU_SEPARATOR", nil)
("Rotate 90" "logoMaker_rotate(form)")
("Mirror" "logoMaker_mirror(form)")
))
axlUICmdPopupSet(popup)
popupGray = axlUIPopupDefine(nil, '(
("Done", "axlDBTransactionCommit(mark), axlFinishEnterFun(), continue = nil")
("Cancel", "axlDBTransactionRollback(mark), axlCancelEnterFun(), continue = nil")
("MENU_SEPARATOR", nil)
("Rotate 90" "logoMaker_rotate(form)")
("Mirror" "logoMaker_mirror(form)")
))
axlUIPopupSet(popupGray)
logoMaker_setDynamicsRotMir(form)
while(continue
event = axlEnterEvent(list('PICK), nil, nil)
caseq(event ->type
(PICK
pt = event ->xy
rot = axlFormGetField(form, "rotation")
mirror = axlFormGetField(form, "mirror")
when(substring(axlFormGetField(form, "subclass"), -6) == "BOTTOM", mirror = !mirror)
axlDBTransactionMark(mark), axlUIPopupSet(popup), form ->oopsCount = form ->oopsCount + 1
sym = when(axlDesignType(nil) == "LAYOUT"
symDef = isCallable('axlLoadSymbol) && (axlLoadSymbol("format", symbolName) || axlLoadSymbol("mechanical", symbolName))
if(symDef
then car(axlDBCreateSymbol(list(symbolName, symDef ->type), pt, mirror, rot))
else
symDef = axlDBCreateSymDefSkeleton(list(symbolName, "format"), list(-tad:-tad, x:y))
car(axlDBCreateSymbolSkeleton(list(symbolName, "format"), pt, mirror, rot, nil))
)
)
bmpFile = if(axlFormGetField(form, "mkbitmap")
then sprintf(nil, "%s/%s.bmp", getWorkingDir(), cadr(axlDMFileParts(bitmapFile)))
else bitmapFile
)
when(bmpFile && isFile(bmpFile)
logoMaker_createSym(form, bmpFile, ?symbol sym, ?location pt, ?angle float(rot), ?mirror mirror)
)
when(axlFormGetField(form, "fixed"), axlDBAddProp(sym, list("FIXED" t)))
)
(DONE, axlDBTransactionCommit(mark), axlFinishEnterFun(), continue = nil)
(CANCEL, axlDBTransactionRollback(mark), axlCancelEnterFun(), continue = nil)
)
)
axlUIPopupSet(nil), axlUICmdPopupSet(nil)
axlClearDynamics()
gc(); Do a garbage collection in an attempt to save memory.
)
))
defun( logoMaker_rotate (form)
axlFormSetField(form, "rotation", mod(axlFormGetField(form, "rotation") + 90, 360))
logoMaker_setDynamicsRotMir(form)
)
defun( logoMaker_mirror (form)
axlFormSetField(form, "mirror", !axlFormGetField(form, "mirror"))
logoMaker_setDynamicsRotMir(form)
)
defun( logoMaker_createSym (form, bitmapFile @key symbol, (location 0:0), (angle 0.0), mirror)
let((x0, y0, turnPolicy, turdSize, alphaMax, longCurve, unitN, blackLevel, inverted, layer
logoWidth, logoHeight, options, cmd, svgFile, p, ln, data, width, height, (scaleX 1), (scaleY 1)
pathEnd, void, dx, dy, ch1, x1, y1, x, y, ptR, rpath, oShape, pt1, pt2, pt3, pt4, xb, yb, xc, yc, pts, shapes)
x0 = car(location), y0 = cadr(location)
turnPolicy = axlFormGetField(form, "turnpolicy"); string
turdSize = axlFormGetField(form, "turdsize"); integer
alphaMax = axlFormGetField(form, "alphamax"); real
longCurve = if(axlFormGetField(form, "longcurve"), sprintf(nil, "-O %2.2f", axlFormGetField(form, "opttolerance")), "-n")
unitN = axlFormGetField(form, "unitn"); integer
blackLevel = axlFormGetField(form, "blacklevel")
inverted = if(axlFormGetField(form, "inverted"), "-i", "")
layer = strcat(axlFormGetField(form, "class"), "/", axlFormGetField(form, "subclass"))
sprintf(logoWidth, "%gmm", axlMKSConvert(axlFormGetField(form, "width"), form ->designUnits, "mm"))
sprintf(logoHeight, "%gmm", axlMKSConvert(axlFormGetField(form, "height"), form ->designUnits, "mm"))
sprintf(svgFile, "%s.svg", cadr(axlDMFileParts(bitmapFile)))
sprintf(options "-z %s -t %d -a %2.2f %s -u %d -k %2.2f %s -W %s -H %s"; -A %d
turnPolicy, turdSize, alphaMax, longCurve, unitN, blackLevel, inverted, logoWidth, logoHeight)
sprintf(cmd, "%s/potrace -s -o \"%s\" %s \"%s\"", form ->potracePath, svgFile, options, bitmapFile)
if(!numberp(axlRunBatchDBProgram("potrace", cmd, ?noUnload t, ?silent t, ?noProgress t)) then
form ->tempFiles = unique(cons(svgFile, form ->tempFiles))
p = axlDMOpenFile("MISC", svgFile, "r")
while(gets(ln, p)
cond(
(substring(ln, 1, 13) == "<svg version="
data = parseString(substring(ln, 6), " \n")
while(substring(ln, -2) != ">\n", gets(ln, p), nconc(data, parseString(ln " >\n")))
width = substring(car(setof(d, data, substring(d, 1, 6) == "width=")), 7)
height = substring(car(setof(d, data, substring(d, 1, 7) == "height=")), 8)
width = readstring(car(parseString(width, "\"p")))
height = readstring(car(parseString(height, "\"p")))
)
(substring(ln, 1, 13) == "<g transform="
data = parseString(substring(ln, 14), " \"\n")
scaleX = readstring(cadr(parseString(cadr(data), "(,)"))) * if(mirror, -1, 1)
scaleY = readstring(caddr(parseString(cadr(data), "(,)"))) * -1
)
(substring(ln, 1, 9) == "<path d=\""
data = parseString(substring(ln, 10), " \"/>\n")
while(substring(ln, -4) != "\"/>\n", gets(ln, p), nconc(data, parseString(ln " \"/>\n")))
pathEnd = void = nil
while(data
when(pathEnd, void = t)
dx = car(data), dy = cadr(data), data = cddr(data)
pathEnd = and(substring(dy, -1) == "z", dy = substring(dy, 1, strlen(dy) - 1))
ch1 = substring(dx, 1, 1)
unless(ch1 == "-" || numberp(readstring(ch1)), dx = substring(dx, 2), cmd = ch1)
dx = readstring(dx), dy = readstring(dy)
cond(
(cmd == "M"
x1 = x = dx, y1 = y = dy
ptR = axlGeoRotatePt(angle, x0 + PtsToUnits(x) * scaleX:y0 + PtsToUnits(y) * scaleY, x0:y0); [mirror]
rpath = axlPathStart(list(ptR))
)
(cmd == "m"
x1 = x = x + dx, y1 = y = y + dy
ptR = axlGeoRotatePt(angle, x0 + PtsToUnits(x) * scaleX:y0 + PtsToUnits(y) * scaleY, x0:y0); [mirror]
rpath = axlPathStart(list(ptR))
)
(cmd == "c"
pt1 = x:y, xb = x + dx, yb = y + dy, pt2 = xb:yb
dx = readstring(car(data)), dy = readstring(cadr(data)), data = cddr(data)
xc = x + dx, yc = y + dy, pt3 = xc:yc
dx = readstring(car(data)), dy = cadr(data), data = cddr(data)
pathEnd = and(substring(dy, -1) == "z", dy = substring(dy, 1, strlen(dy) - 1))
dy = readstring(dy), x = x + dx, y = y + dy, pt4 = x:y
pts = BezierToPath(pt1, pt2, pt3, pt4)
foreach(pt, cdr(pts)
ptR = axlGeoRotatePt(angle, x0 + PtsToUnits(car(pt)) * scaleX:y0 + PtsToUnits(cadr(pt)) * scaleY, , x0:y0)
axlPathLine(rpath, nil, ptR)
)
)
(cmd == "l"
x = x + dx, y = y + dy
ptR = axlGeoRotatePt(angle, x0 + PtsToUnits(x) * scaleX:y0 + PtsToUnits(y) * scaleY, x0:y0)
axlPathLine(rpath, nil, ptR)
)
(t, warn("Character '%s' in svg file not recognised\n", cmd), x = x + dx, y = y + dy)
)
when(pathEnd
unless(x == x1 && y == y1
ptR = axlGeoRotatePt(angle, PtsToUnits(x1) * scaleX:PtsToUnits(y1) * scaleY, x0:y0)
axlPathLine(rpath, nil, ptR)
)
if(void, axlDBCreateVoid(oShape, rpath), oShape = axlDBCreateOpenShape(rpath, t, layer, nil, symbol))
)
); while data
shapes = cons(car(axlDBCreateCloseShape(oShape)), shapes)
);path
)
)
axlDMClose(p)
else
logoMaker_Help("potrace failure")
axlFormMsg(form, "warnmsg", "potrace command failed")
)
shapes
))
defun( logoMaker_Help (section @optional header)
let((helpTxt, sectionList, title, filePath, p, (author "dave.elder@gmail.com"))
helpTxt = logoMaker_helpTxt()
sectionList = assoc(section, helpTxt)
if(sectionList then
title = cadr(sectionList) || "Logo Maker Help File"
rexCompile(" ")
filePath = getWorkingDir()
unless(isWritable(filePath), filePath = getShellEnvVar("TEMP"))
filePath = strcat(filePath, "/", "taithelp_", rexReplace(section, "", 0), ".txt")
p = outfile(filePath, "w")
if(p then
unless(header, header = section)
fprintf(p, "%s\n%s\n\n", header, buildString(vectorToList(makeVector(strlen(header), "=")), ""))
foreach(ln, caddr(sectionList), fprintf(p, "%s\n", ln))
fprintf(p, "\n\nFor further information please email: %s\n", author)
close(p)
axlUIViewFileCreate(filePath, title, t)
else axlUIConfirm("Could not create help text temporary file")
)
else axlUIConfirm("Help text not found")
)
))
defun( logoMaker_helpTxt ()
list(
list("About This Utility", "General Information"
list(
"Purpose:"
" Create graphics such as logos in Allegro based on bitmap-type images."
""
" When using Allegro Layout, scaled and/or mirrored graphics are attached"
" to a mechanical symbol. This symbol may be subsequently moved."
""
" By running this utility in the Allegro symbol editor you can create a"
" (non-scalable) symbol for placing in your company library."
""
"Limitations:"
" potrace creates a Scalable Vector Graphics (SVG) file containing lines and"
" bezier curves to define the outline of polygons. This Skill program draws"
" the Bezier curves using a series of straight lines. In some instances this"
" will result in Allegro shapes that do not represent the original image well."
" I will happily incorporate a circular arc fitting routine if a maths guru"
" has time to develop one and sends it to me."
""
"Platforms tested (It should work on others too):"
" Windows 7, XP, Linux RH"
""
"Allegro verions:"
" 16.6, 17.2"
""
"Acknowledgements:"
" This utility relies heavily on the potrace open source code generously"
" provided by Peter Selinger."
" http://potrace.sourceforge.net"
" It uses:"
" The ImageMagick Command Line Image File Converter available as a"
" free download from ImageMagick. It is pre-installed on many systems."
" http://www.imagemagick.org"
""
"WARRANTY:"
" None. This program is free software and is available as-is"
" It may not work in all environments. There is no formal support."
""
"Modification and Distribution"
" You may redistribute and/or modify this utility. However, in the open-source"
" spirit in which this is provided to the Cadence community, any improvements"
" and enhancements should be submitted to the originator for inclusion in future"
" releases."
)
)
list("Pre-processing Options", "Pre-process Image"
list(
"This section uses the mkbitmap program."
""
"Mkbitmap is a program distributed with Potrace which can be used to pre-process"
"the input for better tracing behavior on greyscale and color images."
""
"mkbitmap reads an image, and applies one or more of the following operations to"
"it, in this order: inversion, highpass filtering, scaling, and thresholding."
"Each operation can be individually controlled and turned on or off."
""
"The principal use of mkbitmap is to convert color or greyscale images into a"
"format suitable as input for other programs, particularly the tracing program"
"potrace."
""
"It is particularly useful for converting scanned line art, such as cartoons,"
"handwritten text, etc., to high-resolution bilevel images."
""
"Highpass filtering can be used to ensure that features such as lines and text"
"are preserved, while at the same time compensating for uneven background."
""
"Scaling is important because a scanned greyscale image contains more visual"
"detail than a bilevel image at the same resolution. By scaling the image to"
"a higher resolution (using interpolation) before thresholding it, some of this"
"detail is preserved."
""
"Thresholding means converting a greyscale image to a bilevel image using only"
"black and white pixels. Pixels that are darker than a certain threshold value"
"are converted to black."
""
"Optional inversion is useful if the input image shows bright features on dark"
"background, such as a picture of chalk drawings on a blackboard."
""
"Invert"
" Invert the input image."
" If this option is chosen, it is applied to the image before any other"
" operation. It is used to deal with white-on-black images, such as"
" photographs of chalk drawings on a blackboard."
" Note that the behavior of this option is not in general the same as"
" inverting the output bitmap, unless the thresholding value is also"
" inverted."
""
"Apply Highpass filtering"
" Apply a highpass filter to the image."
" This filter is approximately Gaussian and non-directional."
" The effect is to preserve small detail while compensating for unevenness"
" in the background. The parameter n is a radius (in pixels) which"
" corresponds approximately to the size of details which should be"
" preserved. More precisely, the filter is implemented by subtracting a"
" blurred version of the image from the original image."
" The parameter n is equal to the standard deviation of the blur."
" The output of the filtering step is a normalized image whose average"
" brightness is exactly 0.5."
" The default filter radius is 4."
""
"Scale"
" Scale the image by an integer factor n>0."
" Scaling is done after highpass filtering, but before the thresholding"
" step."
" A scaling factor of 1 indicates that no scaling is to be done."
" Otherwise, interpolation is used to fill in the in-between pixels."
" If the output of mkbitmap is to be used as input to a tracing program"
" such as potrace, a scaling factor of 2 is recommended. This preserves"
" the right amount of detail for the tracing algorithm to work well."
" If a scaling factor of 1 is used, too much detail is lost."
" If a scaling factor of 3 or higher is used, the interpolation tends to"
" \"invent\" detail which was not present in the original image, thus"
" preventing potrace from doing a good job."
""
"Cubic interpolation"
" Use cubic interpolation when scaling to a higher resolution."
" It is slower than linear interpolation, but leads to better results."
""
"Linear interpolation"
" Use linear interpolation when scaling to a higher resolution."
" This is slightly faster, but less nice, than the default cubic"
" interpolation."
""
"Bilevel conversion"
" If this option is selected, processing continues using the conversion"
" threshold."
""
"Greymap conversion"
" Disable bilevel conversion."
" If this option is selected, processing stops after the scaling step"
" and a greymap is output."
""
"Bilevel conversion threshold"
" Set the threshold grey value for bilevel conversion."
" The parameter n is a brightness value between 0 for black and 1 for"
" white. Any pixels below this brightness will be converted to black"
" (thus, smaller values of n will lead to whiter output)."
""
"http://potrace.sourceforge.net/mkbitmap.1.html"
)
)
list("Tracing Options", "Potrace utility options"
list(
"This section uses the Potrace program."
""
"Potrace is a utility for tracing a bitmap, which means, transforming a bitmap"
"into a smooth, scalable image."
"The input is a bitmap, which means, a pixel-based image composed of the two"
"colors black and white only. The resulting image is not \"jaggy\" like a bitmap,"
"but smooth. It can then be rendered at any resolution."
""
"The input image should only use the two colors black and white. If other pixel"
"values appear in the input, they will be converted to black and white using a"
"simple threshold method."
""
"Algorithm options:"
"------------------"
"Turnpolicy"
" Specifies how to resolve ambiguities in path decomposition."
" Must be one of black, white, right, left, minority, majority, or random."
" Default is minority."
""
"Speckle size"
" Suppress speckles of up to this many pixels."
""
"Corner threshold"
" Set the corner threshold parameter. The default value is 1."
" The smaller this value, the more sharp corners will be produced."
" If this parameter is negative, then no smoothing will be performed and"
" the output is a polygon. The largest useful value is 4/3 or 1.334,"
" which suppresses all corners and leads to completely smooth output."
""
"Curve optimization"
" Normally potrace tries to join adjacent Bezier curve segments when this"
" is possible. Disabling this behavior, results in a larger file size."
""
"Curve optimization tolerance"
" The default value is 0.2. Larger values allow more consecutive Bezier"
" curve segments to be joined together in a single segment, at the expense"
" of accuracy."
""
"Output quantization"
" Coordinates in the output are rounded to 1/unit pixels."
" The default of 10 usually gives good results."
""
"Output options:"
"---------------"
"Blacklevel"
" Set the threshold level for converting input images to bitmaps."
" The potrace algorithm expects a bitmap, thus all pixels of the input"
" images are converted to black or white before processing begins. Pixels"
" whose brightness is less than n are converted to black, all other pixels"
" to white."
" Here n is a number between 0 and 1. One case is treated specially: if"
" the input is in an indexed color format with exactly 2 colors, then the"
" blacklevel is ignored and the darker of the two colors is mapped to"
" black."
" Note: the method used by potrace for converting greymaps to bitmaps is"
" very crude; much better results can be obtained if a separate program,"
" such as mkbitmap, is used for this purpose. In particular, mkbitmap,"
" which is distributed with potrace, has the ability to scale and"
" interpolate the image before thresholding, which results in much better"
" preservation of detail."
""
"Invert"
" Invert the input bitmap before processing."
""
"http://potrace.sourceforge.net/potrace.1.html"
)
)
list("Plugins", "Plugin Options"
(list
"The LogoMaker Skill utility makes use of three free utilities that need to be"
"installed before use:"
""
"1/ potrace - Download the executable from:"
" http://potrace.sourceforge.net/download"
" http://potrace.sourceforge.net/download/potrace-1.8.win32-i386.zip (Windows)"
""
" set the POTRACE_PATH environment variable to the path to the executable"
" eg. C:\\utils\\potrace"
" If using Program Files folder (not advised) use [drive letter]:\\Progra~1\\potrace eg. c:\\Progra~1\\potrace"
""
"2/ mkbitmap - Distributed with the potrace utility"
""
"3/ ImageMagick"
" - Download and install the precompiled release suitable for your platform from:"
" This may not be necessary as ImageMagick is pre-installed on many systems"
" http://www.imagemagick.org/script/binary-releases.php"
" http://www.imagemagick.org/download/binaries/ImageMagick-6.5.3-Q16-windows.zip"
""
" set the MAGICK_HOME environment variable to the path to the convert executable"
" eg. C:\\utils\\ImageMagick\\ImageMagick-6.5.1-3"
)
)
list("Valid Image Types", nil
list(
"Over 100 bitmap image types (such as jpeg and gif) can be processed."
"However, this image type could not be converted to a bitmap."
"Try opening the file in a graphics tool and saving it as a simple bitmap."
)
)
list("mkbitmap failure", nil
list(
"The mkbitmap utility failed to convert this image to a portable bitmap format."
"The most likely explanation is that the source file is too large, invalid or corrupt."
"Try opening it in a graphics editor such as MS paint and saving it as a monochrome bitmap"
)
)
list("potrace failure", nil
list("The potrace utility failed to convert this image to svg.")
)
list("ImageMagick bmp conversion failure", nil
list("The ImageMagick utility failed to convert this image to a bmp.")
)
)
)
defun( PtsToUnits (pts), axlMKSConvert(pts / 0.072, "mils"))
defun( Lerp (pt1, pt2, dt); Simple linear interpolation between two points. dt between 0 and 1.0
list(car(pt1) + (car(pt2) - car(pt1)) * dt, cadr(pt1) + (cadr(pt2) - cadr(pt1)) * dt)
)
defun( BezierPt (a, b, c, d, dt) ;Evaluate a point on a bezier-curve. dt between 0 and 1.0
let((ab, bc, cd, abbc, bccd)
ab = Lerp(a, b, dt); point between a and b
bc = Lerp(b, c, dt); point between b and c
cd = Lerp(c, d, dt); point between c and d
abbc = Lerp(ab, bc, dt); point between ab and bc
bccd = Lerp(bc, cd, dt); point between bc and cd
Lerp(abbc, bccd, dt); point on the bezier-curve
))
defun( BezierToPath (pt1 pt2 pt3 pt4 @optional (n 10))
let((dt, pts)
for(i, 1, n - 1
dt = i / float(n - 1)
pts = cons(BezierPt(pt1, pt2, pt3, pt4, dt), pts)
)
reverse(pts)
))
;Create HEADER lines on a form:
defun( logoMaker_formStart (formWidth, title)
let((fStr)
sprintf(fStr, "FILE_TYPE=FORM_DEFN VERSION=2\nFORM\nFIXED\nPORT %d 10\n", formWidth)
sprintf(fStr, "%sHEADER \"%s\"\n", fStr, title)
sprintf(nil, "%sTILE\n", fStr)
))
defun( logoMaker_formMessage (fStr, msgLabel @key (level 0), (msgString "%s"))
sprintf(nil, "%s\nMESSAGE %s %d \"%s\"\n", fStr, msgLabel, level, msgString)
)
defun( logoMaker_formPopupDefine (fStr, popupName @optional popPairs)
let((pair, (popPairsStr ""), tdisplay, tdispatch, (chCount 0))
sprintf(fStr, "%s\nPOPUP <%s> ", fStr, popupName)
unless(popPairs, popPairs = '(""))
while(popPairs && chCount < 6000
pair = car(popPairs), popPairs = cdr(popPairs)
if(listp(pair) then tdisplay = car(pair), tdispatch = cadr(pair) else tdisplay = tdispatch = pair)
when(numberp(tdisplay), sprintf(tdisplay, "%d", tdisplay))
when(numberp(tdispatch), sprintf(tdispatch, "%d", tdispatch))
sprintf(popPairsStr, "%s \"%s\" \"%s\"", popPairsStr, tdisplay, tdispatch)
chCount = chCount + strlen(popPairsStr)
when(popPairs && chCount < 6000, sprintf(popPairsStr, "%s%s", popPairsStr, ","))
when(strlen(popPairsStr) > 60 && popPairs
sprintf(fStr, "%s%s\\\n", fStr, popPairsStr)
popPairsStr = ""
)
)
sprintf(nil, "%s%s.\n", fStr, popPairsStr)
))
;Create a STRFILLIN line on a form. A title is optional:
defun( logoMaker_formStrFillin (fStr, label, fillinWidth, fillinLength, x1, lineNo @key title, x2, pop, options)
if(title then
x2 = x2 || strlen(title) + x1 - 2
fStr = logoMaker_formText(fStr, title, x1, lineNo ?label strcat(label, "_title"))
else x2 = x1
)
pop = logoMaker_formOptionalString("POP", pop)
options = logoMaker_formOptionsString(options)
sprintf(nil, "%s\nFIELD %s\n\tFLOC %d %d\n\tSTRFILLIN %d %d\n%s%sENDFIELD\n", fStr, label, x2, lineNo, fillinWidth, fillinLength, options, pop)
)
;Create a Button on a form:
defun( logoMaker_formButton (fStr, label, buttonTxt, x, lineNo, w, h)
sprintf(nil, "%s\nFIELD %s\n\tFLOC %d %d\n\tMENUBUTTON \"%s\" %d %d\nENDFIELD\n\n", fStr, label, x, lineNo, buttonTxt, w, h)
)
;Create a Checklist item on a form:
defun( logoMaker_formChecklistItem (fStr, label, x, lineNo, displayTxt @key (radioLabel ""))
unless(radioLabel == "", radioLabel = strcat(" \"", radioLabel, "\""))
sprintf(nil, "%s\nFIELD %s\n\tFLOC %d %d\n\tCHECKLIST \"%s\"%s\nENDFIELD\n", fStr, label, x, lineNo, displayTxt, radioLabel)
)
;Create a Thumbnail on a form:
defun( logoMaker_formThumbnail (fStr, label, x, lineNo, w, h @key options)
options = logoMaker_formOptionsString(options)
sprintf(nil, "%s\nFIELD %s\n\tTHUMBNAIL\n\tFLOC %d %d\n\tFSIZE %d %d\n%sENDFIELD\n", fStr, label, x, lineNo, w, h, options)
)
;Create a Group on a form:
defun( logoMaker_formGroup (fStr, displayTxt, x, lineNo, w, h @key (label ""))
unless(label == "", label = strcat(" ", label))
sprintf(nil, "%s\nGROUP \"%s\"%s\n\tFLOC %d %d\n\tFSIZE %d %d\nENDGROUP\n", fStr, displayTxt, label, x, lineNo, w, h)
)
defun( logoMaker_formText (fStr, txt, x, lineNo @key options (label ""))
options = logoMaker_formOptionsString(options)
sprintf(nil, "%s\nTEXT \"%s\" %s\n\tTLOC %d %d\n%sENDTEXT\n", fStr, txt || "", label, x, lineNo, options)
)
defun( logoMaker_formIntSlideBar (fStr, label, fillinWidth, fillinLength, x1, lineNo, minVal, maxVal @key title, fillInX, value, units)
if(title then
fillInX = fillInX || strlen(title) + x1 - 2
fStr = logoMaker_formText(fStr, title, x1, lineNo ?label strcat(label, "_title"))
else fillInX = x1
)
if(value then sprintf(value, "\tVALUE %L\n", value) else value = "")
sprintf(fStr, "%s\nFIELD %s\n\tFLOC %d %d\n\tINTSLIDEBAR %d %d\n\tMIN %d\n\tMAX %d\n%sENDFIELD\n\n"
fStr, label, fillInX, lineNo, fillinWidth, fillinLength, minVal, maxVal, value)
when(units, fStr = logoMaker_formText(fStr, units, fillInX + fillinWidth + 5, lineNo, ?label strcat(label, "Units")))
fStr
)
;Create a REALFILLIN line on a form. A title is optional:
defun( logoMaker_formRealFillin (fStr, label, fillinWidth, fillinLength, x1, lineNo @key title, fillInX, minVal, maxVal, value, units)
if(title then
fillInX = fillInX || strlen(title) + x1 - 2
fStr = logoMaker_formText(fStr, title, x1, lineNo, ?label strcat(label, "_title"))
else fillInX = x1
)
if(minVal then sprintf(minVal, "\tMIN %L\n", minVal) else minVal = "")
if(maxVal then sprintf(maxVal, "\tMAX %L\n", maxVal) else maxVal = "")
if(value then sprintf(value, "\tVALUE %L\n", value) else value = "")
sprintf(fStr, "%s\nFIELD %s\n\tFLOC %d %d\n\tREALFILLIN %d %d\n%s%s%sENDFIELD\n"
fStr, label, fillInX, lineNo, fillinWidth, fillinLength, minVal, maxVal, value)
when(units, fStr = logoMaker_formText(fStr, units, fillInX + fillinWidth + 2, lineNo, ?label strcat(label, "Units")))
fStr
)
;Create an LONGFILLIN line on a form that only accepts integers. A title is optional:
defun( logoMaker_formIntegerFillin (fStr, label, fillinWidth, fillinLength, x1, lineNo @key title, fillInX, titleLabel, minVal, maxVal, value, pop, units)
if(title then
fillInX = fillInX || strlen(title) + x1 - 2
fStr = logoMaker_formText(fStr, title, x1, lineNo ?label titleLabel || strcat(label, "_title"))
else fillInX = x1
)
if(minVal then sprintf(minVal, "\tMIN %L\n", minVal) else minVal = "")
if(maxVal then sprintf(maxVal, "\tMAX %L\n", maxVal) else maxVal = "")
if(value then sprintf(value, "\tVALUE %L\n", value) else value = "")
pop = logoMaker_formOptionalString("POP", pop)
sprintf(fStr, "%s\nFIELD %s\n\tFLOC %d %d\n\tLONGFILLIN %d %d\n%s%s%s%sENDFIELD\n"
fStr, label, fillInX, lineNo, fillinWidth, fillinLength, minVal, maxVal, value, pop)
when(units, fStr = logoMaker_formText(fStr, units, fillInX + fillinWidth + if(pop == "", 2, 5), lineNo, ?label strcat(label, "Units")))
fStr; Return either the string (if an embedded form) or the port.
)
;Create a Popup on a form:
defun( logoMaker_formPop (fStr, label, popDef, titlex, lineNo, enumset @key options)
options = logoMaker_formOptionsString(options)
sprintf(nil, "%s\nFIELD %s\n\tFLOC %d %d\n\tENUMSET %d\n\tPOP \"%s\"\n%sENDFIELD\n", fStr, label, titlex, lineNo, enumset, popDef, options)
)
;Create a Coloured box on a form:
defun( logoMaker_formColor (fStr, label, x, lineNo, w, h)
sprintf(nil, "%s\nFIELD %s\n\tFLOC %d %d\n\tCOLOR %d %d\nENDFIELD\n", fStr, label, x, lineNo, w, h)
)
defun( logoMaker_formEnd (fStr), sprintf(nil, "%s\nENDTILE\nENDFORM\n", fStr))
/****************************************************************/
defun( logoMaker_formCreateEmbedded (fStr, callBackFn)
let((embFID)
embFID = gensym()
axlFormCreate(embFID, list(sprintf(nil, "embeddedForm_%s", embFID), list(fStr)), '(E INNER), callBackFn, nil)
))
defun( logoMaker_formSetLayerSubColour (form, field, layer)
axlFormSetField(form, field, if(axlIsLayer(layer) && axlIsVisibleLayer(layer), axlLayerGet(layer) ->color, 'black))
axlFormSetField(form, field, axlIsVisibleLayer(layer))
)
/****************************************************************/
/* Form String creation functions */
/****************************************************************/
defun( logoMaker_formOptionsString (options)
when(listp(options), options = buildString(options, " "))
unless(options == "", options = strcat("\tOPTIONS ", options, "\n"))
options
)
defun( logoMaker_formOptionalString (fieldName, value)
cond(
(stringp(value), sprintf(nil, "\t%s %s\n", fieldName, value))
(value, sprintf(nil, "\t%s\n", fieldName))
(t, "")
)
)