The Omni Group
These forums are now read-only. Please visit our new forums to participate in discussion. A new account will be required to post in the new forums. For more info on the switch, see this post. Thank you!

Go Back   The Omni Group Forums > OmniGraffle > OmniGraffle General
FAQ Members List Calendar Search Today's Posts Mark Forums Read

 
Script: Make Circles of N Block Arrows Thread Tools Search this Thread Display Modes
For cases which need more flexibility than the Curved Block Arrow stencils, the following script can be adapted to generate block arcs with arrows at both ends, and/or circles of single-line arrows.

(Probably helpful to select the generated shapes and use Edit > Shapes > Make Custom Shape)

Code:
property pTitle : "Make Circles of N Block Arrows"
property pVer : "0.07"

--  Copyright © 2010, Robin Trew
--  All rights reserved.
-- 	
-- 	Redistribution and use in source and binary forms, with or without modification, 
-- 	are permitted provided that the following conditions are met:
-- 	
-- 		- Redistributions of source code must retain the above copyright notice, 
-- 		  this list of conditions and the following disclaimer.
-- 		- Redistributions in binary form must reproduce the above copyright notice, 
-- 		  this list of conditions and the following disclaimer in the documentation 
-- 		  and/or other materials provided with the distribution.
-- 		
-- 	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
-- 	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
-- 	THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
-- 	IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 
-- 	ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-- 	 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
-- 	 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
-- 	 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
-- 	 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-- ver 0.05 Takes account of any rotation of the template shape to determine where first arrow starts
-- ver 0.06 Corrects radius of non-block arrows (when inner or outer fit has been specified)

property pstrHelp : "
	Number			Number of arrows
	a=N				Number of arrows
	w=N			width of block arrows
	h=true|false		header arrow ?
	t=true|false		tail arrrow ?
	b=true|false		block arrows ?
	r=c				radius=corner
	r=e				radius=mid edge
	f=i				fit=inside
	f=o				fit=outside
	f=m			fit=mid arrow
	?				Help
"

property pstrPrompt : "Number of arrows ?
Width of block arrows ?
Head and or tail arrow ?
Block arrows ? (or simple line arrows)
Circle radius:
	to corner of shape ?
	to mid-edge of shape ?
Circle fit:
	 fits inside shape ?
	 fits outside shape ?
	 covers edge of shape ?

HELP ?	(enter a question mark)
"

property plngArrows : 4
property plngRadius : 150

property pblnBlockArrow : true
property pArrowWidth : 20

property peMidEdgeRadius : 1
property peCornerRadius : 2
property peInnerFit : 1
property peOuterFit : 2
property peMidArrowFit : 3

property pRadiusMatch : 2 -- peCornerRadius
property pFit : 2 -- peOuterFit

property pArcAnglePoints : 0.25

property pHeadArrow : true
property pTailArrow : false


property pstrLineArrowHead : "FilledArrow"
property plstLineArrowColor : {1.0, 0.0, 0.0}
property prLineArrowThickness : 2
property pblnLineArrowShadow : true

property plstBlockArrowLineColor : {34081, 38505, 51776}

-- User data keys (adjust if they happen to clash with keys already in use)

property pGap : 1 / 400

property pDeg : 180 / pi

-- FUNCTION:
-- CREATES A CYCLE DIAGRAM, IN WHICH A SET OF SHAPES ARE LINKED IN A CIRCLE
-- BY ARC ARROWS

on run
	tell application id "OGfl"
		activate
		
		set oShape to self
		try
			oShape
		on error
			if my isFullScreen("OGfl") then
				set oWin to window 2
			else
				set oWin to window 1
			end if
			if id of oWin < 0 then return
			
			set lstSeln to selection of oWin
			if length of lstSeln < 1 then
				display dialog "First position, size, and select a rectangle, then run again." buttons {"OK"} default button "OK" with title pTitle & "  ver. " & pVer
				return
			end if
			set oShape to item 1 of lstSeln
		end try
		
		set strReturned to "?"
		repeat until strReturned ≠ "?"
			tell oShape
				
				
				set strDefault to "arrows=" & (plngArrows as string) & space & "w=" & pArrowWidth & " h=" & pHeadArrow & " t=" & pTailArrow & " b=" & pblnBlockArrow
				if pRadiusMatch = peCornerRadius then
					set strDefault to strDefault & " radius=corner"
				else
					set strDefault to strDefault & " radius=edge"
				end if
				if pFit = peInnerFit then
					set strDefault to strDefault & " fit=innner"
				else
					set strDefault to strDefault & " fit=outer"
				end if
				
				try
					tell (display dialog pstrPrompt default answer strDefault with title pTitle & tab & pVer)
						set strReturned to text returned
					end tell
				on error
					return
				end try
				
				set {dlm, my text item delimiters} to {my text item delimiters, " "}
				set lstParts to text items of strReturned
				set lngParts to length of lstParts
				if lngParts < 1 then return
				set my text item delimiters to dlm
				
				set {dlm, my text item delimiters} to {my text item delimiters, "="}
				repeat with iPart from 1 to lngParts
					set strPart to item iPart of lstParts
					set lstKeyValue to text items of strPart
					if length of lstKeyValue = 2 then
						set {strKey, strvalue} to lstKeyValue
						if strKey begins with "a" then
							try
								set plngArrows to (strvalue as integer)
							on error
								display alert strvalue & " could not be read as an integer"
								return
							end try
						else if strKey begins with "h" then
							try
								set pHeadArrow to strvalue as boolean
							on error
								display dialog strvalue & " could not be interpreted as a boolean value ..."
								return
							end try
						else if strKey begins with "t" then
							try
								set pTailArrow to strvalue as boolean
							on error
								display dialog strvalue & " could not be interpreted as a boolean value ..."
								return
							end try
						else if strKey begins with "w" then
							try
								set pArrowWidth to strvalue as number
							on error
								display dialog strvalue & " could not be interpreted as a number ..."
								return
							end try
						else if strKey begins with "b" then
							try
								set pblnBlockArrow to strvalue as boolean
							on error
								display dialog strvalue & " could not be interpreted as a boolean value ..."
								return
							end try
						else if strKey begins with "r" then
							if strvalue begins with "c" then
								set pRadiusMatch to peCornerRadius
							else
								set pRadiusMatch to peMidEdgeRadius
								
							end if
						else if strKey begins with "f" then
							if strvalue begins with "i" then
								set pFit to peInnerFit
							else if strvalue begins with "o" then
								set pFit to peOuterFit
							else
								set pFit to peMidArrowFit
							end if
						end if
					else
						if strPart begins with "?" then
							display dialog pstrHelp buttons {"OK"} default button "OK" with title pTitle & "  ver. " & pVer
						else
							try
								set plngArrows to (strPart as integer)
							on error
								display alert strpars & " could not be read as an integer"
								return
							end try
						end if
					end if
				end repeat
				set my text item delimiters to dlm
				
				if plngArrows < 1 or plngArrows > 100 then
					display alert "Number of arrows must be in range 1-100 ..."
					return
				end if
				
				if strReturned ≠ "?" then
					set oCanvas to its canvas
					set automatic layout of layout info of oCanvas to false
					
					
					set {rX, rY} to origin
					set {rWidth, rHeight} to size
					set rRotn to rotation
					
					
					-- DETERMINE RADIUS AS A FUNCTION OF  radius type and fit type
					if rWidth ≥ rHeight then
						set plngRadius to (rWidth / 2)
					else
						set plngRadius to (rHeight / 2) + pArrowWidth
					end if
					
					if pRadiusMatch = peCornerRadius then
						set plngRadius to ((rWidth / 2) ^ 2 + (rHeight / 2) ^ 2) ^ 0.5
					else
						if rWidth ≥ rHeight then
							set plngRadius to (rHeight / 2)
						else
							set plngRadius to (rWidth / 2)
						end if
					end if
					
					if pblnBlockArrow then
						if pFit = peInnerFit then
							set plngRadius to plngRadius - pArrowWidth / 2
						else if pFit = peOuterFit then
							set plngRadius to plngRadius + pArrowWidth / 2
						end if
					end if
					
					set lstCenter to {rX + rWidth / 2, rY + rHeight / 2}
					set rTheta to rRotn
					set rDelta to 360 / plngArrows
					set rNext to rTheta + rDelta
					
					-- DRAW A CURVED ARC (ARROW) BETWEEN EACH SUCCESSIVE PAIR OF SHAPES
					repeat with i from 1 to plngArrows
						set oArc to my MakeArc(pblnBlockArrow, lstCenter, plngRadius, rTheta, rNext, ¬
							pArrowWidth, pArcAnglePoints, pHeadArrow, pTailArrow, oCanvas)
						if oArc is not missing value then
							tell oArc
								--set value of user data item pstrArcData to "{From:" & idLast & ", To:" & idNext & "}"
								set idArc to its id
							end tell
						end if
						set rTheta to rNext
						set rNext to rTheta + rDelta
					end repeat
				end if
			end tell
		end repeat
	end tell
end run

-- CENTER, RADIUS, FROM DEGREES, TO DEGREES, WIDTH, 
-- BEZIER POINTS PER DEGREE OF ARC, HEAD ARROW, TAIL ARROW
on MakeArc(pblnBlockArrow, {rX, rY}, rRadius, rFrom, rTo, rWidth, rPointsPerDegree, blnHeadArrow, blnTailArrow, oCanvas)
	-- (CURRENTLY A SIMPLE FUNCTION OF THE ARROW WIDTH)
	set {rStart, rEnd} to {rFrom, rTo}
	
	if pblnBlockArrow then
		-- CALCULATE THE LENGTH OF ANY ARROWS 
		if (blnHeadArrow or blnTailArrow) then
			set rArrow to (rWidth / (pi * rRadius)) * 180
			if blnHeadArrow then set rEnd to rTo - rArrow
			if blnTailArrow then set rStart to rFrom + rArrow
		end if
	end if
	
	-- AND THE AMOUNT OF ARC WHICH IT TRAVELS
	set rArc to rEnd - rStart
	if rArc < 0 then set rArc to (rEnd + 360) - rStart
	
	-- HOW MANY POINTS WILL WE USE TO DRAW THE SHAFT ?
	set lngPoints to (rArc * rPointsPerDegree) as integer
	if lngPoints > 0 then
		set rDelta to rArc / lngPoints
		
		if pblnBlockArrow then
			set oGraphic to BlockArrow(oCanvas, {rX, rY}, rFrom, rTo, rStart, rEnd, rDelta, lngPoints, rWidth, blnHeadArrow, blnTailArrow, rRadius)
		else
			set oGraphic to ArcArrow(oCanvas, {rX, rY}, rFrom, rTo, rDelta, lngPoints, blnHeadArrow, blnTailArrow, rRadius)
		end if
		
		return oGraphic
	else
		return missing value
	end if
end MakeArc

on ArcArrow(oCanvas, {rX, rY}, rFrom, rTo, rDelta, lngPoints, blnHeadArrow, blnTailArrow, rRadius)
	set rTheta to rFrom
	set lstPoints to {}
	repeat with i from 1 to lngPoints
		set end of lstPoints to {rX + (rRadius * (sin(rTheta))), rY - rRadius * (cos(rTheta))}
		set rTheta to rTheta + rDelta
	end repeat
	set end of lstPoints to {rX + (rRadius * (sin(rTheta))), rY - rRadius * (cos(rTheta))}
	
	-- 	property pstrLineArrowHead : "FilledArrow"
	-- property plstLineArrowColor : {1.0, 0.0, 0.0}
	-- property plngLineArrowThickness : 2
	-- property pblnLineArrowShadow : true
	
	
	tell application id "OGfl"
		tell oCanvas
			set recStyle to {draws stroke:true, thickness:prLineArrowThickness, draws shadow:pblnLineArrowShadow, stroke color:plstLineArrowColor}
			if blnHeadArrow then set recStyle to recStyle & {head type:pstrLineArrowHead}
			if blnTailArrow then set recStyle to recStyle & {tail type:pstrLineArrowHead}
			set oLine to (make new line with properties {point list:lstPoints, line type:curved} & recStyle)
		end tell
	end tell
	return oLine
end ArcArrow

on BlockArrow(oCanvas, {rX, rY}, rFrom, rTo, rStart, rEnd, rDelta, lngPoints, rWidth, blnHeadArrow, blnTailArrow, rRadius)
	
	-- GET THE INNER AND OUTER RADII OF THE BLOCK ARROW
	set rW to rWidth / 2
	set {rRadOut, rRadin} to {rRadius + rW, rRadius - rW}
	
	-- COLLECT THE POINTS FOR THE OUTER AND INNER FLANKS OF THE ARROW
	set {lstOuter, lstInner} to {{}, {}}
	set rTheta to rStart
	repeat with i from 1 to lngPoints
		set {dX, dY} to {sin(rTheta), cos(rTheta)}
		set end of lstOuter to {rX + (rRadOut * dX), rY - rRadOut * dY}
		set end of lstInner to {rX + (rRadin * dX), rY - rRadin * dY}
		set rTheta to rTheta + rDelta
	end repeat
	set {dX, dY} to {sin(rTheta), cos(rTheta)}
	set end of lstOuter to {rX + (rRadOut * dX), rY - rRadOut * dY}
	set end of lstInner to {rX + (rRadin * dX), rY - rRadin * dY}
	
	-- MAKE HEAD AND/OR TAIL ARROWS IF REQUIRED
	set {lstHead, lstTail} to {{}, {}}
	set {rInnerBarb, rOuterBarb} to {rRadius - rWidth, rRadius + rWidth}
	-- TAIL ARROW ?
	if blnTailArrow then
		-- inner barb, tip, outer barb
		set lstTail to {{rX + (rInnerBarb * (sin(rStart))), rY - rInnerBarb * (cos(rStart))}, ¬
			{rX + (rRadius * (sin(rFrom))), rY - rRadius * (cos(rFrom))}, ¬
			{rX + (rOuterBarb * (sin(rStart))), rY - rOuterBarb * (cos(rStart))}}
	end if
	-- HEAD ARROW ?
	if blnHeadArrow then
		set lstHead to {{rX + (rOuterBarb * (sin(rEnd))), rY - rOuterBarb * (cos(rEnd))}, ¬
			{rX + (rRadius * (sin(rTo))), rY - rRadius * (cos(rTo))}, ¬
			{rX + (rInnerBarb * (sin(rEnd))), rY - rInnerBarb * (cos(rEnd))}}
	end if
	
	-- JOIN THE SHAFT TO ANY ARROW TIPS
	set lstPoints to (lstOuter & lstHead & reverse of lstInner & lstTail)
	
	-- GATHER THE POINTS IN THE TRIPLET FORMAT EXPECTED FOR CUSTOM SHAPES
	set oFirstPoint to item 1 of lstPoints
	set lstBezier to {oFirstPoint, oFirstPoint}
	repeat with i from 2 to length of lstPoints
		set oPoint to item i of lstPoints
		set lstBezier to lstBezier & {oPoint, oPoint, oPoint}
	end repeat
	set end of lstBezier to oFirstPoint
	
	-- AND RETURN A CUSTOM SHAPE
	tell application id "OGfl"
		tell oCanvas to set oShape to make new shape with properties {point list:lstBezier, draws stroke:true, stroke color:plstBlockArrowLineColor}
		set notes of oShape to "From " & rFrom & "º" & return & "To " & rTo & "º"
	end tell
	return oShape
end BlockArrow



-- If Lion is running, are we in full screen mode ?
on isFullScreen(strAppID)
	tell application "Finder" to set blnPreLion to (version < "10.7")
	if blnPreLion then
		return false
	else
		tell application id "sevs"
			set lstApps to application processes where creator type = strAppID
			if length of lstApps < 1 then return false
			set lstWins to windows of first item of lstApps
			if length of lstWins < 1 then return false
			return value of attribute "AXFullScreen" of item 1 of lstWins
		end tell
	end if
end isFullScreen

-- on sin(rDegrees)
-- 	(do shell script "echo 'echo $((sin(" & (rDegrees / 180) * pi & ")))' | ksh") as number
-- end sin
-- 
-- on cos(rDegrees)
-- 	(do shell script "echo 'echo $((cos(" & (rDegrees / 180) * pi & ")))' | ksh") as number
-- end cos
-- 
-- on sin(rDegrees)
-- 	(do shell script "echo 's(" & (rDegrees / 180) * pi & ")' | bc -l") as number
-- end sin
-- 
-- on cos(rDegrees)
-- 	(do shell script "echo 'c(" & (rDegrees / 180) * pi & ")' | bc -l") as number
-- end cos


-- BASIC TRIG FUNCTIONS PUBLISHED BY OTHERS
-- COULD USE THE KSH SHELL, BUT THESE ARE PROBABLY FASTER ...

-- Trig functions from:
-- http://lists.apple.com/archives/applescript-users/2004/Feb/msg00939.html

on cos(x) -- degrees
	local myCos, numerator, denominator, factor
	
	set myCos to 0
	if (x = 90) or (x = 270) then
		set myCos to 0
	else
		set x to (x - (((x / 360) div 1) * 360)) * (pi / 180)
		set {myCos, numerator, denominator, factor} to {0, 1, 1, -(x ^ 2)}
		repeat with i from 2 to 40 by 2
			set myCos to myCos + numerator / denominator
			set numerator to numerator * factor
			set denominator to denominator * i * (i - 1)
		end repeat
	end if
	return myCos
end cos
-- 
-- ----------------------------
on sin(x) -- degrees
	local mySin, numerator, denomintator, factor
	
	set mySin to 0
	if (x = 180) or (x = 360) then
		set mySin to 0
	else
		set x to (x - (((x / 360) div 1) * 360)) * (pi / 180)
		set {mySin, numerator, denominator, factor} to {0, x, 1, -(x ^ 2)}
		repeat with i from 3 to 40 by 2
			set mySin to mySin + numerator / denominator
			set numerator to numerator * factor
			set denominator to denominator * i * (i - 1)
		end repeat
	end if
	return mySin
end sin

Last edited by RobTrew; 2012-02-25 at 03:21 AM..
 
Updated the script (above) to add a dialog for adjusting the number, width, direction and position of the arrows.

Also added a stencil containing a tool which uses this code as a script action.
 
 


Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes


Similar Threads
Thread Thread Starter Forum Replies Last Post
Stencil: Custom circles of arrows (Shape with script Action) RobTrew OmniGraffle Extras 1 2013-07-29 07:28 PM
Stencil: Curved block arrows RobTrew OmniGraffle Extras 3 2012-02-23 02:13 AM
How to make and drag arrows? John Boyden OmniGraffle General 4 2009-10-11 08:11 AM
New stencil: curly braces, cloud shapes, block arrows snoopy67 OmniGraffle Extras 0 2008-02-05 08:06 AM
script? to make iCal event pjb iCal Sync 6 2007-06-13 02:07 PM


All times are GMT -8. The time now is 11:49 AM.


Powered by vBulletin® Version 3.8.7
Copyright ©2000 - 2024, vBulletin Solutions, Inc.