<> <> <> <> <> <> <> DIRECTORY CD, CDInstances, CDBasics, CDRects, CDSequencer, CDCallSpecific, CDCommandOps, CDOps, CDOrient, CDProperties, CDViewer, TerminalIO, ViewerClasses, ViewerOps; CDCutWireCommands: CEDAR PROGRAM IMPORTS CDInstances, CDBasics, CDCommandOps, CDOps, CDOrient, CDProperties, CDSequencer, CDViewer, TerminalIO, CDRects, ViewerOps = BEGIN CutInstance: PROC [design: CD.Design, inst: CD.Instance, from, to: CD.Position, gridding: CD.Number_1] = { NearlyRoundToLambda: PROC[x: CD.Number] RETURNS[r: CD.Number] = { r _ (x/gridding)*gridding; }; cutPos: CD.Position; L1, L2, W1, W2: CD.Number; wireLeft, wireRight: CD.Object; inter: CD.Position; -- the intersection of the two lines, if any, and the center of the wire oldRect: CD.Rect _ CDInstances.InstRectI[inst]; -- the bounding box oldSize: CD.Position _ CDBasics.SizeOfRect[oldRect]; vertical: BOOL _ oldSize.y > oldSize.x; dx: CD.Number _ to.x-from.x; dy: CD.Number _ to.y-from.y; ctr: CD.Position _ CDBasics.Center[oldRect]; <<--Test if vector parallel to wire>> IF vertical AND (dx=0) OR (NOT vertical) AND (dy=0) THEN RETURN; <<--Now we can divide and find the intersection of lines (not yet segments)>> gridding _ MAX[1, gridding]; inter _ IF vertical THEN [ctr.x, (from.y+(dy*(ctr.x-from.x)/dx))/gridding*gridding] ELSE [(dx*(ctr.y-from.y)/dy+from.x)/gridding*gridding, ctr.y]; <<--Monier-Sindhu theorem: if inter is in cursorBox and in oldRect, then it is the intersection of segments>> IF NOT (CDBasics.InsidePos[inter, CDBasics.ToRect[from, to]] AND CDBasics.InsidePos[inter, CDBasics.Extend[oldRect, -1]]) THEN RETURN; cutPos _ IF vertical THEN [oldRect.x1, inter.y] ELSE [inter.x, oldRect.y1]; IF vertical THEN { L1 _ cutPos.y - oldRect.y1; L2 _ oldSize.y - L1; W1 _ W2 _ oldSize.x; } ELSE { L1 _ L2 _ oldSize.y; W1 _ cutPos.x - oldRect.x1; W2 _ oldSize.x - W1; }; wireLeft _ CDRects.CreateRect[CDOrient.OrientedSize[[W1, L1], inst.orientation], inst.ob.layer]; wireRight _ CDRects.CreateRect[CDOrient.OrientedSize[[W2, L2], inst.orientation], inst.ob.layer]; CDOps.IncludeInstance[design, CDInstances.NewInstI[wireLeft, CDBasics.BaseOfRect[oldRect], inst.orientation, TRUE, CDProperties.DCopyProps[inst.properties]], FALSE]; CDOps.IncludeInstance[design, CDInstances.NewInstI[wireRight, cutPos, inst.orientation, TRUE, CDProperties.DCopyProps[inst.properties]], FALSE]; CDOps.RemoveInstance[design, inst]; }; DoAllCuts: PROC [design: CD.Design, from, to: CD.Position, gridding: CD.Number _ 1] = { <<--The mouse track defines the cut vector. Let's define the "spine" of a wire as the centerline, roughly in the middle (lambda) and following the visible length, i.e. the longer edge, not the CD length. Then we cut the wire at the position where the spine meets the cut vector. The cut always reduces the visible length of the wire. (Pff, not easy to describe without drawing!)>> <<--A vertical wire freshly drawn has even (0) orientation.>> FOR list: CD.InstanceList _ CDOps.InstList[design], list.rest WHILE list#NIL DO IF list.first.selected AND list.first.ob.class.wireTyped THEN CutInstance[design, list.first, from, to, gridding]; ENDLOOP; }; CutWireComm: PROC [comm: CDSequencer.Command] = { grid: CD.Number _ GetGrid[comm]; TerminalIO.WriteRopes["cut selected with grid ", CDCommandOps.LambdaRope[grid, comm.design.technology.lambda], "\n"]; DoAllCuts[design: comm.design, from: comm.sPos, to: comm.pos, gridding: grid] }; GetGrid: PROC [comm: CDSequencer.Command] RETURNS [g: CD.Number_0] = { <<--figures out grid used for a particular design or viewer>> v: ViewerClasses.Viewer _ CDViewer.GetViewer[comm]; IF v#NIL THEN { WITH ViewerOps.GetViewer[v, $Grid] SELECT FROM ri: REF INT => g _ ri^; ENDCASE => NULL; }; IF g<=0 THEN g _ comm.design.technology.lambda; }; CDSequencer.ImplementCommand[$SplitWireS, CutWireComm]; CDSequencer.ImplementCommand[$CutWireS, CutWireComm]; END.