GGScraps.tioga
Last edited by Bier on July 29, 1985 1:26:59 pm PDT
Pieces of code which will probably never be used again, but you never know.
GGMouseEventImpl
SameJointAsLastTime: PROC [thisTraj: Traj, thisJointNum: NAT, gargoyleData: GargoyleData] RETURNS [BOOL] = CHECKED {
RETURN[
gargoyleData.hitTest.responsibleFor # NIL
AND gargoyleData.hitTest.responsibleFor.traj = thisTraj
AND gargoyleData.hitTest.responsibleFor.start = thisJointNum
AND gargoyleData.hitTest.responsibleFor.end = thisJointNum
];
};
NearestJoint: PROC [scene: Scene, worldPt: Point, tolerance: REAL] RETURNS [result: Traj, index: NAT] = {
Returns traj = NIL if no joints are found within tolerance.
d2, d2guess: REAL;
joint: INT;
trajGen: TrajectoryGenerator;
trajGen ← GGObjects.TrajsInScene[scene];
FOR traj: Traj ← GGObjects.NextTraj[trajGen], GGObjects.NextTraj[trajGen] UNTIL trajs = NIL DO
[joint, d2] ← GGObjects.NearestJoint[traj, worldPt, tolerance];
IF joint # -1 THEN {
result ← traj;
index ← joint;
GOTO NearFound;
};
REPEAT
NearFound => {
FOR traj2: Traj ← GGObjects.NextTraj[trajGen], GGObjects.NextTraj[trajGen] UNTIL traj2 = NIL DO
[joint, d2guess] ← GGObjects.NearestJoint[traj2, worldPt, tolerance];
IF joint # -1 THEN {
IF d2guess < d2 THEN {
result ← traj2;
index ← joint;
d2 ← d2guess;
};
};
ENDLOOP;
};
FINISHED => {result ← NIL};
ENDLOOP;
};
August 2, 1985 2:31:39 pm PDT
GGObjectsImpl.mesa
Hit Testing
NearestJoint: PUBLIC PROC [traj: Traj, testPoint: Point, tolerance: REAL] RETURNS [index: INT, d2: REAL] = CHECKED {
Finds the joint which is closest to testPoint. If there are several, it returns one of them. If none, index = -1.
thisPos: Point;
d2guess: REAL;
hiJoint: NAT ← HiJoint[traj];
FOR i: INT IN [0..hiJoint] DO
thisPos ← FetchJointPos[traj, i];
d2 ← GGVector.DistanceSquared[thisPos, testPoint];
IF d2 < tolerance*tolerance THEN {
index ← i;
GOTO NearFound;
};
REPEAT
NearFound => {
FOR j: INT IN [index+1..hiJoint] DO
thisPos ← FetchJointPos[traj, j];
d2guess ← GGVector.DistanceSquared[thisPos, testPoint];
IF d2guess < tolerance*tolerance THEN {
d2 ← d2guess;
index ← j;
};
ENDLOOP;
};
FINISHED => {index ← -1; d2 ← 0.0};
ENDLOOP;
};
NearJoints: PUBLIC PROC [traj: Traj, testPoint: Point, tolerance: REAL] RETURNS [indexes: LIST OF NAT, distancesSquared: LIST OF REAL] = CHECKED {
Finds all of the joints of traj which are within tolerance (and their distances from testPoint). If none, indexes = NIL.
thisPos: Point;
d2: REAL;
indexes ← NIL;
distancesSquared ← NIL;
FOR i: NAT IN [0..HiJoint[traj]] DO
thisPos ← FetchJointPos[traj, i];
d2 ← GGVector.DistanceSquared[thisPos, testPoint];
IF d2 < tolerance*tolerance THEN {
indexes ← CONS[i, indexes];
distancesSquared ← CONS[d2, distancesSquared];
};
ENDLOOP;
};
IsNearTraj: PUBLIC PROC [traj: Traj, testPoint: Point, tolerance: REAL] RETURNS [near: BOOL, howNear: REAL] = CHECKED {
For now, we just look for near joints.
indexes: LIST OF NAT;
distancesSquared: LIST OF REAL;
[indexes, distancesSquared] ← NearJoints[traj, testPoint, tolerance];
IF indexes = NIL THEN {
near ← FALSE; howNear ← 0.0; RETURN;
}
ELSE {
near ← TRUE;
howNear ← distancesSquared.first;
FOR dList: LIST OF REAL ← distancesSquared.rest, dList.rest UNTIL dList = NIL DO
howNear ← MIN[dList.first, howNear];
ENDLOOP;
};
};
IsNearOutline: PUBLIC PROC [outline: Outline, testPoint: Point, tolerance: REAL] RETURNS [near: BOOL, howNear: REAL] = {
howNearGuess: REAL;
nearGuess: BOOL;
FOR children: LIST OF Traj ← outline.children, children.rest UNTIL children = NIL DO
[near, howNearGuess] ← IsNearTraj[children.first, testPoint, tolerance];
IF near THEN GOTO FoundNear;
REPEAT
FoundNear => {
howNear ← howNearGuess;
FOR children2: LIST OF Traj ← children.rest, children2.rest UNTIL children2 = NIL DO
[nearGuess, howNearGuess] ← IsNearTraj[children2.first, testPoint, tolerance];
IF nearGuess THEN howNear ← MIN[howNearGuess, howNear];
ENDLOOP;
};
FINISHED => {
near ← FALSE; howNear ← 0.0;
};
ENDLOOP;
};
IsNearCluster: PUBLIC PROC [cluster: Cluster, testPoint: Point, tolerance: REAL] RETURNS [near: BOOL, howNear: REAL] = {
howNearGuess: REAL;
nearGuess: BOOL;
FOR children: LIST OF REF ANY ← cluster.children, children.rest UNTIL children = NIL DO
WITH children.first SELECT FROM
outline: Outline => {
[near, howNearGuess] ← IsNearOutline[outline, testPoint, tolerance];
IF near THEN GOTO FoundNear
};
subCluster: Cluster => {
[near, howNearGuess] ← IsNearCluster[subCluster, testPoint, tolerance];
IF near THEN GOTO FoundNear;
};
ENDCASE => ERROR;
REPEAT
FoundNear => {
howNear ← howNearGuess;
FOR children2: LIST OF REF ANY ← children.rest, children2.rest UNTIL children2 = NIL DO
WITH children2.first SELECT FROM
outline: Outline => {
[nearGuess, howNearGuess] ← IsNearOutline[outline, testPoint, tolerance];
IF nearGuess THEN howNear ← MIN[howNearGuess, howNear];
};
subCluster: Cluster => {
[nearGuess, howNearGuess] ← IsNearCluster[subCluster, testPoint, tolerance];
IF nearGuess THEN howNear ← MIN[howNearGuess, howNear];
};
ENDCASE => ERROR;
ENDLOOP;
};
FINISHED => {
near ← FALSE; howNear ← 0.0;
};
ENDLOOP;
};
From GGSequenceImpl, Bier on February 25, 1986 11:56:00 am PST:
CombineSequences: PUBLIC PROC [new, old: Sequence] RETURNS [union: Sequence, newMinusOld: Sequence] = {
IF new.traj # old.traj THEN ERROR;
IF new.traj.role = open THEN [union, newMinusOld] ← CombineSequencesOpen[new, old]
ELSE {
[union, newMinusOld] ← CombineSequencesOpen[new, old];
IF new.all THEN union ← new; -- kludge until CombineSequencesClosed is done
IF old.all THEN union ← old;
};
};
InitializeCombine: PROC [new, old: Sequence, newJointBits, oldJointBits, newSegmentBits, oldSegmentBits: BitVector] = {
segGen: SegmentGenerator;
jointGen: JointGenerator;
Initialize
FOR i: NAT IN [0..GGObjects.HiSegment[new.traj]] DO
newSegmentBits[i] ← FALSE;
oldSegmentBits[i] ← FALSE;
ENDLOOP;
FOR i: NAT IN [0..GGObjects.GGObjects.HiJoint[new.traj]] DO
newJointBits[i] ← FALSE;
oldJointBits[i] ← FALSE;
ENDLOOP;
Set new Bits.
segGen ← SegmentsInSequence[new];
FOR next: GGObjects.SegAndIndex ← NextSegmentAndIndex[segGen], NextSegmentAndIndex[segGen] UNTIL next.seg = NIL DO
newSegmentBits[next.index] ← TRUE;
ENDLOOP;
jointGen ← JointsInSequence[new];
FOR index: INT ← NextJoint[jointGen], NextJoint[jointGen] UNTIL index = -1 DO
newJointBits[index] ← TRUE;
ENDLOOP;
Set old Bits.
segGen ← SegmentsInSequence[old];
FOR next: GGObjects.SegAndIndex ← NextSegmentAndIndex[segGen], NextSegmentAndIndex[segGen] UNTIL next.seg = NIL DO
oldSegmentBits[next.index] ← TRUE;
ENDLOOP;
jointGen ← JointsInSequence[old];
FOR index: INT ← NextJoint[jointGen], NextJoint[jointGen] UNTIL index = -1 DO
oldJointBits[index] ← TRUE;
ENDLOOP;
};
CombineSequencesOpen: PROC [new, old: Sequence] RETURNS [union: Sequence, newMinusOld: Sequence] = {
unionJointBit, diffJointBit, unionSegmentBit, diffSegmentBit, lastSegmentBit, lastJointBit: BOOL;
newJointBits, oldJointBits, newSegmentBits, oldSegmentBits: BitVector;
start, end: NAT;
newSegmentBits ← NEW[BitVectorObj[new.traj.segCount]];
oldSegmentBits ← NEW[BitVectorObj[new.traj.segCount]];
newJointBits ← NEW[BitVectorObj[new.traj.segCount + 1]];
oldJointBits ← NEW[BitVectorObj[new.traj.segCount + 1]];
Make the bitmaps reflect the sequences
InitializeCombine[new, old, newJointBits, oldJointBits, newSegmentBits, oldSegmentBits];
Combine the segment bit vectors.
FOR i: NAT IN [0..GGObjects.HiSegment[new.traj]] DO
unionSegmentBit ← newSegmentBits[i] OR oldSegmentBits[i];
diffSegmentBit ← newSegmentBits[i] AND NOT oldSegmentBits[i];
newSegmentBits[i] ← unionSegmentBit;
oldSegmentBits[i] ← diffSegmentBit;
ENDLOOP;
Combine the joint bit vectors.
FOR i: NAT IN [0..GGObjects.HiJoint[new.traj]] DO
unionJointBit ← newJointBits[i] OR oldJointBits[i];
diffJointBit ← newJointBits[i] AND NOT oldJointBits[i];
newJointBits[i] ← unionJointBit;
oldJointBits[i] ← diffJointBit;
ENDLOOP;
Convert union back to sequences.
union ← GGObjects.CreateEmpty[new.traj];
lastSegmentBit ← FALSE;
FOR i: NAT IN [0..GGObjects.HiSegment[new.traj]] DO
IF NOT lastSegmentBit AND newJointBits[i] THEN { -- The start of a new part
start ← i;
};
IF lastSegmentBit AND NOT newJointBits[i] THEN ERROR;
lastJointBit ← newJointBits[i];
IF lastJointBit AND NOT newSegmentBits[i] THEN { -- the end of the current part
end ← i;
GGObjects.AddPart[union, start, end];
};
lastSegmentBit ← newSegmentBits[i];
REPEAT
FINISHED => {
IF NOT lastSegmentBit AND newJointBits[GGObjects.HiSegment[new.traj] + 1] THEN {
The last joint is a lone part.
start ← end ← GGObjects.HiSegment[new.traj] + 1;
GGObjects.AddPart[union, start, end];
}
ELSE IF lastSegmentBit THEN {
end ← GGObjects.HiSegment[new.traj] + 1;
GGObjects.AddPart[union, start, end];
};
};
ENDLOOP;
Convert diff back to sequences.
newMinusOld ← GGObjects.CreateEmpty[new.traj];
lastSegmentBit ← FALSE;
FOR i: NAT IN [0..GGObjects.HiSegment[new.traj]] DO
IF NOT lastSegmentBit AND oldJointBits[i] THEN { -- The start of a new part
start ← i;
};
IF lastSegmentBit AND NOT oldJointBits[i] THEN {
It is time to kludge. The LIST OF JointPair data structure isn't rich enough to represent this situation. Pretend that lastSegmentBit was never true and close the currently open part.
end ← i-1;
GGObjects.AddPart[newMinusOld, start, end];
};
lastJointBit ← oldJointBits[i];
IF lastJointBit AND NOT oldSegmentBits[i] THEN { -- the end of the current part
end ← i;
GGObjects.AddPart[newMinusOld, start, end];
};
IF NOT lastJointBit AND oldSegmentBits[i] THEN {
Another chance to kludge. (See kludge comment above). We pretend that this segment doesn't exist.
lastSegmentBit ← FALSE;
}
ELSE lastSegmentBit ← oldSegmentBits[i];
REPEAT
FINISHED => {
IF NOT lastSegmentBit AND oldJointBits[GGObjects.HiSegment[new.traj] + 1] THEN {
The last joint is a lone part.
start ← end ← GGObjects.HiSegment[new.traj] + 1;
GGObjects.AddPart[newMinusOld, start, end];
}
ELSE IF lastSegmentBit AND NOT oldJointBits[GGObjects.HiSegment[new.traj] + 1] THEN {
One last kludge.
end ← GGObjects.HiSegment[new.traj];
GGObjects.AddPart[newMinusOld, start, end];
}
ELSE IF lastSegmentBit THEN {
end ← GGObjects.HiSegment[new.traj] + 1;
GGObjects.AddPart[newMinusOld, start, end];
};
};
ENDLOOP;
};
CombineSequencesClosed: PROC [new, old: Sequence] RETURNS [union: Sequence, newMinusOld: Sequence] = {
Not yet done.
unionJointBit, diffJointBit, unionSegmentBit, diffSegmentBit, lastSegmentBit, lastJointBit: BOOL;
newJointBits, oldJointBits, newSegmentBits, oldSegmentBits: BitVector;
start, end: NAT;
newSegmentBits ← NEW[BitVectorObj[new.traj.segCount]];
oldSegmentBits ← NEW[BitVectorObj[new.traj.segCount]];
newJointBits ← NEW[BitVectorObj[new.traj.segCount]];
oldJointBits ← NEW[BitVectorObj[new.traj.segCount]];
Make the bitmaps reflect the sequences
InitializeCombine[new, old, newJointBits, oldJointBits, newSegmentBits, oldSegmentBits];
Combine the segment bit vectors.
FOR i: NAT IN [0..GGObjects.HiSegment[new.traj]] DO
unionSegmentBit ← newSegmentBits[i] OR oldSegmentBits[i];
diffSegmentBit ← newSegmentBits[i] AND NOT oldSegmentBits[i];
newSegmentBits[i] ← unionSegmentBit;
oldSegmentBits[i] ← diffSegmentBit;
ENDLOOP;
Combine the joint bit vectors.
FOR i: NAT IN [0..GGObjects.HiJoint[new.traj]] DO
unionJointBit ← newJointBits[i] OR oldJointBits[i];
diffJointBit ← newJointBits[i] AND NOT oldJointBits[i];
newJointBits[i] ← unionJointBit;
oldJointBits[i] ← diffJointBit;
ENDLOOP;
Convert union back to sequences (new = union, old = difference).
union ← GGObjects.CreateEmpty[new.traj];
lastSegmentBit ← newSegmentBits[GGObjects.HiSegment[new.traj]];
FOR i: NAT IN [0..GGObjects.GGObjects.HiSegment[new.traj]] DO
IF NOT lastSegmentBit AND newJointBits[i] THEN { -- The start of a new part
start ← i;
};
IF lastSegmentBit AND NOT newJointBits[i] THEN ERROR;
lastJointBit ← newJointBits[i];
IF lastJointBit AND NOT newSegmentBits[i] THEN { -- the end of the current part
end ← i;
GGObjects.AddPart[union, start, end];
};
lastSegmentBit ← newSegmentBits[i];
REPEAT
FINISHED => {
IF NOT lastSegmentBit AND newJointBits[GGObjects.HiSegment[new.traj] + 1] THEN {
The last joint is a lone part.
start ← end ← GGObjects.HiSegment[new.traj] + 1;
GGObjects.AddPart[union, start, end];
}
ELSE IF lastSegmentBit THEN {
end ← GGObjects.HiSegment[new.traj] + 1;
GGObjects.AddPart[union, start, end];
};
};
ENDLOOP;
Convert diff back to sequences.
newMinusOld ← GGObjects.CreateEmpty[new.traj];
lastSegmentBit ← FALSE;
FOR i: NAT IN [0..GGObjects.HiSegment[new.traj]] DO
IF NOT lastSegmentBit AND oldJointBits[i] THEN { -- The start of a new part
start ← i;
};
IF lastSegmentBit AND NOT oldJointBits[i] THEN {
It is time to kludge. The LIST OF JointPair data structure isn't rich enough to represent this situation. Pretend that lastSegmentBit was never true and close the currently open part.
end ← i-1;
GGObjects.AddPart[newMinusOld, start, end];
};
lastJointBit ← oldJointBits[i];
IF lastJointBit AND NOT oldSegmentBits[i] THEN { -- the end of the current part
end ← i;
GGObjects.AddPart[newMinusOld, start, end];
};
IF NOT lastJointBit AND oldSegmentBits[i] THEN {
Another chance to kludge. (See kludge comment above). We pretend that this segment doesn't exist.
lastSegmentBit ← FALSE;
}
ELSE lastSegmentBit ← oldSegmentBits[i];
REPEAT
FINISHED => {
IF NOT lastSegmentBit AND oldJointBits[GGObjects.HiSegment[new.traj] + 1] THEN {
The last joint is a lone part.
start ← end ← GGObjects.HiSegment[new.traj] + 1;
GGObjects.AddPart[newMinusOld, start, end];
}
ELSE IF lastSegmentBit AND NOT oldJointBits[GGObjects.HiSegment[new.traj] + 1] THEN {
One last kludge.
end ← GGObjects.HiSegment[new.traj];
GGObjects.AddPart[newMinusOld, start, end];
}
ELSE IF lastSegmentBit THEN {
end ← GGObjects.HiSegment[new.traj] + 1;
GGObjects.AddPart[newMinusOld, start, end];
};
};
ENDLOOP;
};
SegmentsInSequence: PUBLIC PROC [seq: Sequence] RETURNS [segGen: SegmentGenerator] = {
start, end: NAT;
IF seq.parts = NIL THEN ERROR;
IF seq.all THEN RETURN[SegmentsInTraj[seq.traj]];
start ← seq.parts.first.start;
end ← seq.parts.first.end;
segGen ← SegmentsInJointPair[seq.traj, start, end];
segGen.nextParts ← seq.parts.rest;
};
SegmentsInJointPair: PUBLIC PROC [traj: Traj, start: NAT, end: INT] RETURNS [segGen: SegmentGenerator] = {
toGo: NAT;
IF start > GGObjects.HiJoint[traj] OR end > GGObjects.HiJoint[traj] OR end < -1 THEN ERROR;
IF traj.role = open THEN {
IF end < start THEN toGo ← 0
ELSE toGo ← end - start;
}
ELSE {
IF start = end THEN toGo ← 0
ELSE IF start < end THEN toGo ← end - start
ELSE toGo ← traj.segCount - start + end;
};
segGen ← NEW[SegmentGeneratorObj ← [
traj: traj,
toGo: toGo,
index: start,
nextParts: NIL
]];
};
JointsInJointPair: PUBLIC PROC [traj: Traj, start: NAT, end: INT] RETURNS [jointGen: JointGenerator] = {
toGo: NAT;
SELECT traj.role FROM
open => {
IF end < start THEN toGo ← 0
ELSE toGo ← end - start + 1;
};
fence, hole => {
IF start = end THEN toGo ← 1
ELSE IF start < end THEN toGo ← end - start + 1
ELSE toGo ← traj.segCount - start + end + 1;
};
ENDCASE => ERROR;
jointGen ← NEW[JointGeneratorObj ← [
traj: traj,
toGo: toGo,
index: start]];
};
NextJoint: PUBLIC PROC [jointGen: JointGenerator] RETURNS [next: INT] = {
start, end: NAT;
toGo: NAT;
UNTIL jointGen.toGo > 0 DO
IF jointGen.nextParts = NIL THEN RETURN[-1];
start ← jointGen.nextParts.first.start;
end ← jointGen.nextParts.first.end;
jointGen.nextParts ← jointGen.nextParts.rest;
IF jointGen.traj.role = open THEN {
IF end < start THEN toGo ← 0
ELSE toGo ← end - start + 1;
}
ELSE {
IF start = end THEN toGo ← 1
ELSE IF start < end THEN toGo ← end - start + 1
ELSE toGo ← jointGen.traj.segCount - end + start - 1;
};
jointGen.toGo ← toGo;
jointGen.index ← start;
ENDLOOP;
next ← jointGen.index;
jointGen.toGo ← jointGen.toGo - 1;
IF jointGen.traj.role = open THEN {
jointGen.index ← jointGen.index + 1;
}
ELSE {
jointGen.index ← (jointGen.index + 1) MOD jointGen.traj.segCount;
};
};
NextSegment: PUBLIC PROC [segGen: SegmentGenerator] RETURNS [next: Segment] = {
start, end: NAT;
toGo: NAT;
UNTIL segGen.toGo > 0 DO
IF segGen.nextParts = NIL THEN RETURN[NIL];
start ← segGen.nextParts.first.start;
end ← segGen.nextParts.first.end;
segGen.nextParts ← segGen.nextParts.rest;
IF segGen.traj.role = open THEN {
IF end < start THEN toGo ← 0
ELSE toGo ← end - start;
}
ELSE {
IF start = end THEN toGo ← 0
ELSE IF start < end THEN toGo ← end - start
ELSE toGo ← segGen.traj.segCount - end + start - 2;
};
segGen.toGo ← toGo;
segGen.index ← start;
ENDLOOP;
next ← GGObjects.FetchSegment[segGen.traj, segGen.index];
segGen.toGo ← segGen.toGo - 1;
IF segGen.traj.role = open THEN {
segGen.index ← segGen.index + 1;
}
ELSE {
segGen.index ← (segGen.index + 1) MOD segGen.traj.segCount;
};
};
Parts of EndSelectJoint, Bier, December 12, 1986 9:35:07 pm PST
SELECT feature.resultType FROM
joint => {
end: BOOL;
seqPart: SequencePart ← NARROW[feature.hitPart];
traj: Traj ← NARROW[feature.shape, Sequence].traj;
SelectJoint[feature, resultPoint, gargoyleData];
jointNum ← seqPart.jointNum;
GGCaret.SitOn[gargoyleData.caret, joint, traj, jointNum];
IF (end ← GGTraj.IsEndJoint[traj, jointNum]) THEN GGError.AppendHerald[gargoyleData.feedback, "End ", begin]; -- an end joint
GGError.PutFHerald[gargoyleData.feedback, IF end THEN end ELSE oneLiner, "Joint %g selected", [integer[jointNum]] ];
gargoyleData.drag.extendMode ← joint;
gargoyleData.drag.trajToExtend ← traj;
};
segment => {
seqPart: SequencePart ← NARROW[feature.hitPart];
segNum, cpNum: NAT;
isACP, end: BOOL;
traj: Traj ← NARROW[feature.shape, Sequence].traj;
[isACP, jointNum, cpNum, segNum] ← SelectJointOrCP[feature, resultPoint, gargoyleData]; -- selects closest joint or CP
IF isACP THEN {
GGError.PutFHerald[gargoyleData.feedback, oneLiner, "Control point %g selected", [integer[cpNum]]];
GGCaret.SitOn[gargoyleData.caret, cp, traj, 999, cpNum, segNum];
}
ELSE {
GGCaret.SitOn[gargoyleData.caret, joint, traj, jointNum];
IF (end ← GGTraj.IsEndJoint[traj, jointNum]) THEN GGError.AppendHerald[gargoyleData.feedback, "End ", begin]; -- an end joint
GGError.PutFHerald[gargoyleData.feedback, IF end THEN end ELSE oneLiner, "Joint %g selected", [integer[jointNum]] ];
};
gargoyleData.drag.extendMode ← joint;
gargoyleData.drag.trajToExtend ← traj;
};
controlPoint => {
seqPart: SequencePart ← NARROW[feature.hitPart];
segNum, cpNum: NAT;
isACP: BOOL;
traj: Traj ← NARROW[feature.shape, Sequence].traj;
SelectJoint[feature, resultPoint, gargoyleData];
[isACP, ----, cpNum, segNum] ← SelectJointOrCP[feature, resultPoint, gargoyleData]; -- better get the CP !!
GGCaret.SitOn[gargoyleData.caret, cp, traj, 999, cpNum, segNum];
GGError.PutFHerald[gargoyleData.feedback, oneLiner, "Control point %g selected", [integer[seqPart.controlPointNum]] ];
gargoyleData.drag.extendMode ← joint; -- we use joint extendMode for controlPoints
gargoyleData.drag.trajToExtend ← traj;
};
slice => {
sliceD: SliceDescriptor ← NEW[SliceDescriptorObj ← [slice: NARROW[feature.shape, SliceDescriptor].slice] ];
[parts: sliceD.parts] ← SelectJointOrCP[feature, resultPoint, gargoyleData];
GGCaret.SitOn[gargoyleData.caret, slice, sliceD];
GGError.AppendHerald[gargoyleData.feedback, Rope.Concat[sliceD.slice.class.describe[sliceD.slice, sliceD.parts], " selected"], oneLiner];
gargoyleData.drag.extendMode ← joint;
gargoyleData.drag.sliceToExtend ← sliceD.slice;
};
ENDCASE => ERROR Problem[msg: "Unexpected feature type"];
GGAlignImpl.BuiltInFilters, Bier, December 12, 1986 11:56:14 pm PST
BuiltInFilters: PUBLIC PROC [triggerBag: TriggerBag, objectBag: ObjectBag, gargoyleData: GargoyleData] = {
Add Alignment Objects to the objectBag, using the triggers in the triggerBag.
jointGen: JointGenerator;
cpGen: ControlPointGenerator;
segGen: SegmentGenerator;
seq: Sequence;
seg: Segment;
anchorFeature: FeatureData;
joint: Joint;
sliceD: SliceDescriptor;
pointGen: PointGenerator;
pointPairGen: PointPairGenerator;
filters: Filters ← gargoyleData.hitTest;
IF gargoyleData.camera.hideAlignments THEN {
GGGravity.FlushObjectBag[objectBag];
RETURN;
};
FOR l: LIST OF FeatureData ← triggerBag.seqs, l.rest UNTIL l = NIL DO
seq ← NARROW[l.first.shape];
Joints as triggers.
jointGen ← GGSequence.JointsInSequence[seq];
FOR i: INT ← GGSequence.NextJoint[jointGen], GGSequence.NextJoint[jointGen] UNTIL i = -1 DO
joint ← GGTraj.FetchJoint[seq.traj, i];
[] ← JointFireRule[joint.point, filters, objectBag];
ENDLOOP;
CPs as triggers.
cpGen ← GGSequence.ControlPointsInSequence[seq];
FOR pad: PointAndDone ← GGSequence.NextControlPoint[cpGen], GGSequence.NextControlPoint[cpGen] UNTIL pad.done DO
[] ← JointFireRule[pad.point, filters, objectBag];
ENDLOOP;
Segments as triggers.
segGen ← GGSequence.SegmentsInSequence[seq];
FOR next: SegAndIndex ← GGSequence.NextSegmentAndIndex[segGen], GGSequence.NextSegmentAndIndex[segGen] UNTIL next.seg = NIL DO
seg ← GGTraj.FetchSegment[seq.traj, next.index];
[] ← SegmentFireRule[next.index, seg.lo, seg.hi, filters, objectBag];
ENDLOOP;
ENDLOOP;
The anchor as a trigger.
anchorFeature ← triggerBag.anchor;
IF anchorFeature # NIL AND GGCaret.Exists[NARROW[anchorFeature.shape, Caret]] THEN {
point: Point;
point ← GGCaret.GetPoint[NARROW[anchorFeature.shape, Caret]];
[] ← JointFireRule[point, filters, objectBag];
};
Triggers from slices.
FOR l: LIST OF FeatureData ← triggerBag.slices, l.rest UNTIL l = NIL DO
sliceD ← NARROW[l.first.shape];
pointGen ← sliceD.slice.class.pointsInDescriptor[sliceD];
FOR next: GGModelTypes.PointAndDone ← sliceD.slice.class.nextPoint[pointGen], sliceD.slice.class.nextPoint[pointGen] UNTIL next.done DO
[] ← JointFireRule[next.point, filters, objectBag];
ENDLOOP;
pointPairGen ← sliceD.slice.class.pointPairsInDescriptor[sliceD];
FOR next: GGModelTypes.PointPairAndDone ← sliceD.slice.class.nextPointPair[pointPairGen], sliceD.slice.class.nextPointPair[pointPairGen] UNTIL next.done DO
[] ← SegmentFireRule[9999, next.lo, next.hi, filters, objectBag];
ENDLOOP;
ENDLOOP;
};
From GGSegmentImplA.mesa on January 19, 1987, Bier
CurveClosestPoint: PROC [seg: Segment, testPoint: Point, tolerance: REAL] RETURNS [point: Point, success: BOOL] = {
GGSegmentTypes.ClosestPointProc
Used for hit testing. Find the nearest point on seg to testPoint.
path: CubicPaths.Path;
IF useBBox THEN {
bigBox: GGBasicTypes.BoundBoxObj ← [seg.bBox.loX-tolerance, seg.bBox.loY-tolerance, seg.bBox.hiX+tolerance, seg.bBox.hiY+tolerance, FALSE, FALSE];
IF NOT PointIsInBox[testPoint, bigBox] THEN RETURN[[0,0], FALSE];
};
GGStatistics.StartInterval[$CurveClosestPoint, GGStatistics.GlobalTable[]];
path ← GetPath[seg];
[point, success] ← GGCubic2.ClosestPointSubdivide[[testPoint.x,testPoint.y], path, 1.0, tolerance];
epsilon should be 0.072, but I will wait until GGCubic2.ClosestPoint is faster.
GGStatistics.StopInterval[$CurveClosestPoint, GGStatistics.GlobalTable[]];
};