From VisualWorks® NonCommercial, 7.4 of December 5, 2005 on January 28, 2006 at 6:34:42 am Katachi Smalltalk false private Smalltalk.* Katachi Katachi KatachiWave Smalltalk Core.Object false none figure eyeCatchers Katachi Katachi Katachi2dWave Smalltalk KatachiWave false none minimalEnclosingCircle samplingFrequency intensitySamples Katachi Katachi Katachi2dHalfCircumferenceWave Smalltalk Katachi2dWave false none Katachi Katachi Katachi2dReverseCircleWave Smalltalk Katachi2dWave false none Katachi Katachi Katachi2dCircleWave Smalltalk Katachi2dWave false none Katachi Katachi Katachi2dNullWave Smalltalk Katachi2dWave false none Katachi Katachi Katachi2dPolygonWave Smalltalk Katachi2dWave false none Katachi Katachi KatachiWave utilities show ^self subclassResponsibility KatachiWave initialize-release initialize eyeCatchers := OrderedCollection new. ^self KatachiWave accessing removeEyeCatcher: aPoint eyeCatchers remove: aPoint eyeCatchers ^eyeCatchers addEyeCatcher: aPoint eyeCatchers add: aPoint figure ^figure addEyeCatchers: aCollection aCollection do: [:eyeCatcher | self addEyeCatcher: eyeCatcher] removeEyeCatchers: aCollection aCollection do: [:eyeCatcher | self removeEyeCatcher: eyeCatcher] KatachiWave comparing similarTo: aKatachiWave "Return similarity between 0.0d and 1.0d." ^self subclassResponsibility KatachiWave private interpretFigure: figureRepresentation ^self subclassResponsibility setFigure: figureRepresentation figure := figureRepresentation. self interpretFigure: figureRepresentation. ^self Katachi2dWave class utilities similarityMatrix: figureCollection " | kCollection | kCollection := OrderedCollection new. kCollection add: Katachi2dPolygonWave example1. kCollection add: Katachi2dPolygonWave example2. kCollection add: Katachi2dPolygonWave example3. Katachi2dWave similarityMatrix: kCollection " | borderColor borderThickness paddingThickness headerBorderThickness cellExtent itemExtent headerRowHeight headerColumnWidth numberOfFigures originX1 originY1 extentX extentY extent image borderGc cellGc slashGc line figure atX atY baseFigure objectFigure similarityDescription similarity phase model textFont textWidth computeSimilarities similarityAccuracy roundedSimilarity text | borderColor := ColorValue black. borderThickness := 1. paddingThickness := 3. headerBorderThickness := 2. cellExtent := 60 @ 78. similarityAccuracy := 0.001d. itemExtent := cellExtent - (paddingThickness * 2 asPoint). headerRowHeight := cellExtent x. headerColumnWidth := cellExtent x * 2. numberOfFigures := figureCollection size. originX1 := borderThickness + headerColumnWidth + headerBorderThickness. originY1 := borderThickness + headerRowHeight + headerBorderThickness. extentX := (cellExtent x + borderThickness) * numberOfFigures + originX1. extentY := (cellExtent y + borderThickness) * numberOfFigures + originY1. extent := extentX @ extentY. cellGc := nil. computeSimilarities := [:progress | textFont := cellGc font. 1 to: numberOfFigures do: [:iy | baseFigure := figureCollection at: iy. atY := (cellExtent y + borderThickness) * (iy - 1) + originY1 + paddingThickness. 1 to: numberOfFigures do: [:ix | progress message: ix printString, '@', iy printString. progress value: (iy - 1) * numberOfFigures + ix - 1 / numberOfFigures squared. objectFigure := figureCollection at: ix. atX := (cellExtent x + borderThickness) * (ix - 1) + originX1 + paddingThickness. similarityDescription := baseFigure similarTo: objectFigure. similarity := similarityDescription at: 1. roundedSimilarity := ((similarity / similarityAccuracy) rounded * similarityAccuracy) asFloat. phase := similarityDescription at: 2. image := objectFigure figureImageExtent: itemExtent - (0 @ textFont height) rotatePhase: phase. cellGc displayImage: image at: atX @ atY. text := roundedSimilarity printString. textWidth := cellGc widthOfString: text. cellGc displayString: text at: (atX + ((itemExtent x - textWidth) quo: 2)) @ (atY + itemExtent y - textFont descent)]]]. image := JunImageUtility imageExtent: extent displayBlock: [:graphicsContext | borderGc := graphicsContext copy. cellGc := graphicsContext copy. borderGc lineWidth: 1. borderGc paint: borderColor. line := Rectangle origin: Point zero extent: extent x @ borderThickness. line displayFilledOn: borderGc. line := Rectangle origin: Point zero extent: borderThickness @ extent y. line displayFilledOn: borderGc. line := Rectangle origin: 0 @ (borderThickness + headerRowHeight) extent: extent x @ headerBorderThickness. line displayFilledOn: borderGc. line := Rectangle origin: (borderThickness + headerColumnWidth) @ 0 extent: headerBorderThickness @ extent y. line displayFilledOn: borderGc. atX := borderThickness + headerColumnWidth. atY := borderThickness + headerRowHeight. slashGc := borderGc copy. slashGc lineWidth: headerBorderThickness. slashGc displayLineFrom: Point zero to: atX @ atY. textFont := slashGc font. text := 'base(self)'. textWidth := slashGc widthOfString: text. atX := borderThickness + paddingThickness. atY := borderThickness + headerRowHeight - paddingThickness - textFont descent. slashGc displayString: text at: atX @ atY. text := 'compare to'. textWidth := slashGc widthOfString: text. atX := borderThickness + headerColumnWidth - paddingThickness - textWidth. atY := borderThickness + paddingThickness + textFont ascent. slashGc displayString: text at: atX @ atY. 1 to: numberOfFigures do: [:ix | figure := figureCollection at: ix. image := figure figureImageExtent: (cellExtent x @ headerRowHeight) - (paddingThickness * 2 asPoint). atX := (cellExtent x + borderThickness) * (ix - 1) + originX1 + paddingThickness. atY := borderThickness + paddingThickness. cellGc displayImage: image at: atX @ atY. atX := (cellExtent x + borderThickness) * (ix - 1) + originX1 + cellExtent x. line := Rectangle origin: atX @ 0 extent: borderThickness @ extent y. line displayFilledOn: borderGc]. 1 to: numberOfFigures do: [:iy | figure := figureCollection at: iy. image := figure showImageExtent: (headerColumnWidth @ cellExtent y) - (paddingThickness * 2 asPoint). atX := borderThickness + paddingThickness. atY := (cellExtent y + borderThickness) * (iy - 1) + originY1 + paddingThickness. cellGc displayImage: image at: atX @ atY. atY := (cellExtent y + borderThickness) * (iy - 1) + originY1 + cellExtent y. line := Rectangle origin: 0 @ atY extent: extent x @ borderThickness. line displayFilledOn: borderGc]. JunProgress new do: [:progress | computeSimilarities value: progress]]. model := JunImageDisplayModel show: image label: 'Katachi similarity matrix'. ^model Katachi2dWave class examples example2 "Katachi2dWave example2" | kCollection | kCollection := OrderedCollection new. kCollection add: (Katachi2dPolygonWave regularPolygon: 3). kCollection add: (Katachi2dPolygonWave regularPolygon: 4). kCollection add: (Katachi2dPolygonWave regularPolygon: 6). kCollection add: (Katachi2dPolygonWave stellatedPolygon: 5). kCollection add: Katachi2dPolygonWave exampleBar. kCollection add: Katachi2dPolygonWave exampleButterfly. kCollection add: Katachi2dPolygonWave exampleCup. kCollection add: Katachi2dPolygonWave exampleDeceivingCup. kCollection add: Katachi2dPolygonWave exampleRightIsoscelesTriangle1. kCollection add: Katachi2dPolygonWave exampleRightIsoscelesTriangle2. kCollection add: Katachi2dPolygonWave exampleShell. kCollection add: Katachi2dCircleWave new. kCollection add: Katachi2dHalfCircumferenceWave new. kCollection add: Katachi2dNullWave new. kCollection add: Katachi2dReverseCircleWave new. Katachi2dWave similarityMatrix: kCollection example1 "Katachi2dWave example1" | kCollection | kCollection := OrderedCollection new. kCollection add: (Katachi2dPolygonWave regularPolygon: 3). kCollection add: (Katachi2dPolygonWave stellatedPolygon: 5). kCollection add: Katachi2dPolygonWave exampleBar. kCollection add: Katachi2dPolygonWave exampleCup. kCollection add: Katachi2dPolygonWave exampleButterfly. kCollection add: Katachi2dPolygonWave exampleRightIsoscelesTriangle1. kCollection add: Katachi2dCircleWave new. Katachi2dWave similarityMatrix: kCollection Katachi2dWave utilities show ^self showExtent: 400 @ 200 showWave | waveImage model | waveImage := self waveImageExtent: 200 @ 200. model := JunImageDisplayModel show: waveImage label: 'Wave of Katachi'. ^model showFigure | figureImage model | figureImage := self figureImageExtent: 200 @ 200. model := JunImageDisplayModel show: figureImage label: 'Base figure of Katachi'. ^model Katachi2dWave initialize-release initialize super initialize. samplingFrequency := self defaultSamplingFrequency. intensitySamples := nil. ^self Katachi2dWave accessing collectSamples: frequency "Return an array whose size is (1 + frequency). The last element is same as the first one." self samplingFrequency = frequency ifTrue: [^self intensitySamples]. ^self rawCollectSamples: frequency samplingFrequency ^samplingFrequency eyeCatchedPhases "Phases from where an eyeCatcher comes in your sight when you look at the center of the minimal enclosing circle." | phases center | phases := Set new: self eyeCatchers size. center := minimalEnclosingCircle center. self eyeCatchers do: [:eyeCatcher | (eyeCatcher equal: center) ifFalse: [phases add: (eyeCatcher - center) theta deg / 360]]. ^phases intensitySamples "Return an array whose size is (1 + samplingFrequency). The last element is same as the first one." intensitySamples isNil ifTrue: [intensitySamples := self rawCollectSamples: self samplingFrequency]. ^intensitySamples copy samplingFrequency: aPositiveInteger aPositiveInteger = samplingFrequency ifFalse: [samplingFrequency := aPositiveInteger. intensitySamples := nil]. self interpretFigure: self figure. ^samplingFrequency intensityAt: phase | index floor ceiling samples dIntensity floorIntensity ceilingIntensity | index := (phase * self samplingFrequency) + 1. floor := index floor. ceiling := index ceiling. samples := self intensitySamples. floorIntensity := samples at: floor. index = floor ifTrue: [^floorIntensity]. ceilingIntensity := samples at: ceiling. dIntensity := ceilingIntensity - floorIntensity. ^(index - floor) * dIntensity + floorIntensity. Katachi2dWave comparing similarTo: aKatachi2dWave " Return an Array having 2 elements. Its first element means similarity. The second one means best rotation of aKatachi2dWave giving the similarity. " | waveComparisonMethod | waveComparisonMethod := #(#RMS #abs) at: 1. "waveComparisonMethod := #(#RMS #abs) at: 2." waveComparisonMethod = #RMS ifTrue: [^self similarRMSDiffTo: aKatachi2dWave]. waveComparisonMethod = #Abs ifTrue: [^self similarAbsDiffTo: aKatachi2dWave]. self error: 'Illegal similarity strategy'. similarRevolveRMSDiffTo: aKatachi2dWave "Similarity with revolving strategy in terms of RMS(root mean square) diff of the two waves." | compareFrequency diffSum mySamples hisSamples diffMax similarity similarityMax similarityMaxAt | compareFrequency := self samplingFrequency. mySamples := self intensitySamples. hisSamples := aKatachi2dWave collectSamples: compareFrequency. diffMax := 2 squared * compareFrequency. similarityMax := -1. 0 to: compareFrequency - 1 do: [:rotateTick | diffSum := 0. self compare: [:p :q | diffSum := diffSum + (p - q) squared] samples: mySamples with: hisSamples indexShift: rotateTick. similarity := 1 - (diffSum / diffMax) sqrt. similarity > similarityMax ifTrue: [ similarityMax := similarity. similarityMaxAt := rotateTick]]. ^Array with: similarityMax with: similarityMaxAt / compareFrequency similarEyeCatchersAbsDiffTo: aKatachi2dWave "Similarity with eye catcher strategy in terms of mean absolute diff of the two waves." | compareFrequency diffSum mySamples myCatchedPhases revolvePhases hisCatchedPhases diffMax similarity similarityMax similarityMaxAt p q phase | compareFrequency := self samplingFrequency. mySamples := self intensitySamples. myCatchedPhases := self eyeCatchedPhases. hisCatchedPhases := aKatachi2dWave eyeCatchedPhases. diffMax := 2 * compareFrequency. similarityMax := -1. revolvePhases := Set new: myCatchedPhases size * hisCatchedPhases size. myCatchedPhases do: [:myCatchedPhase | hisCatchedPhases do: [:hisCatchedPhase | revolvePhases add: (myCatchedPhase - hisCatchedPhase) \\ 1.0]]. revolvePhases isEmpty ifTrue: [revolvePhases add: 0]. revolvePhases do: [:phaseShift | diffSum := 0. 0 to: compareFrequency - 1 do: [:tick | phase := tick / compareFrequency. p := mySamples at: tick + 1. q := aKatachi2dWave intensityAt: (phase - phaseShift) \\ 1.0. diffSum := diffSum + (p - q) abs]. similarity := 1 - (diffSum / diffMax). similarity > similarityMax ifTrue: [ similarityMax := similarity. similarityMaxAt := phaseShift]]. ^Array with: similarityMax with: similarityMaxAt similarRMSDiffTo: aKatachi2dWave "Similarity in terms of RMS(root mean square) diff of the two waves." | revolveSimilarity eyeCatcherSimilarity | revolveSimilarity := self similarRevolveRMSDiffTo: aKatachi2dWave. eyeCatcherSimilarity := self similarEyeCatchersRMSDiffTo: aKatachi2dWave. (revolveSimilarity at: 1) > (eyeCatcherSimilarity at: 1) ifTrue: [^revolveSimilarity] ifFalse: [^eyeCatcherSimilarity] similarEyeCatchersRMSDiffTo: aKatachi2dWave "Similarity with eye catcher strategy in terms of RMS(root mean square) diff of the two waves." | compareFrequency diffSum mySamples myCatchedPhases revolvePhases hisCatchedPhases diffMax similarity similarityMax similarityMaxAt p q phase | compareFrequency := self samplingFrequency. mySamples := self intensitySamples. myCatchedPhases := self eyeCatchedPhases. hisCatchedPhases := aKatachi2dWave eyeCatchedPhases. diffMax := 2 squared * compareFrequency. similarityMax := -1. revolvePhases := Set new: myCatchedPhases size * hisCatchedPhases size. myCatchedPhases do: [:myCatchedPhase | hisCatchedPhases do: [:hisCatchedPhase | revolvePhases add: (myCatchedPhase - hisCatchedPhase) \\ 1.0]]. revolvePhases isEmpty ifTrue: [revolvePhases add: 0]. revolvePhases do: [:phaseShift | diffSum := 0. 0 to: compareFrequency - 1 do: [:tick | phase := tick / compareFrequency. p := mySamples at: tick + 1. q := aKatachi2dWave intensityAt: (phase - phaseShift) \\ 1.0. diffSum := diffSum + (p - q) squared]. similarity := 1 - (diffSum / diffMax) sqrt. similarity > similarityMax ifTrue: [ similarityMax := similarity. similarityMaxAt := phaseShift]]. ^Array with: similarityMax with: similarityMaxAt similarAbsDiffTo: aKatachi2dWave "Similarity in terms of mean absolute diff of the two waves." | revolveSimilarity eyeCatcherSimilarity | revolveSimilarity := self similarRevolveAbsDiffTo: aKatachi2dWave. eyeCatcherSimilarity := self similarEyeCatchersAbsDiffTo: aKatachi2dWave. (revolveSimilarity at: 1) > (eyeCatcherSimilarity at: 1) ifTrue: [^revolveSimilarity] ifFalse: [^eyeCatcherSimilarity] similarRevolveAbsDiffTo: aKatachi2dWave "Similarity with revolving strategy in terms of mean absolute diff of the two waves." | compareFrequency diffSum mySamples hisSamples diffMax similarity similarityMax similarityMaxAt | compareFrequency := self samplingFrequency. mySamples := self intensitySamples. hisSamples := aKatachi2dWave collectSamples: compareFrequency. diffMax := 2 * compareFrequency. similarityMax := -1. 0 to: compareFrequency - 1 do: [:rotateTick | diffSum := 0. self compare: [:p :q | diffSum := diffSum + (p - q) abs] samples: mySamples with: hisSamples indexShift: rotateTick. similarity := 1 - (diffSum / diffMax). similarity > similarityMax ifTrue: [ similarityMax := similarity. similarityMaxAt := rotateTick]]. ^Array with: similarityMax with: similarityMaxAt / compareFrequency Katachi2dWave private defaultSamplingFrequency ^256 showImageExtent: extent | showImage waveImage figureImage waveExtent waveOrigin figureExtent figureOrigin margin | margin := 2. waveOrigin := margin @ margin. waveExtent := (extent x - margin - margin - margin / 2) @ (extent y - margin - margin). figureOrigin := (margin + waveExtent x + margin) @ margin. figureExtent := waveExtent. waveExtent := waveExtent floor. figureExtent := figureExtent floor. waveImage := self waveImageExtent: waveExtent. figureImage := self figureImageExtent: figureExtent. showImage := JunImageUtility imageExtent: extent displayBlock: [:gc | gc displayImage: waveImage at: waveOrigin. gc displayImage: figureImage at: figureOrigin]. ^showImage minimalEnclosingCircleOf: aJun2dPolygon ^self subclassResponsibility rawCollectSamples: frequency | samples phase intensityValue | samples := Array new: (frequency + 1) asInteger. 1 to: samples size do: [:index | phase := (index - 1) / frequency. intensityValue := self depthAt: phase. samples at: index put: intensityValue]. ^samples waveImageExtent: extent | revolutionDirection phaseOffset axisColor waveColor waveThickness samples samplingFreq samplesRotateOffset rotatedSamples chart chartData | revolutionDirection := 1. phaseOffset := JunAngle fromDeg: 90. axisColor := ColorValue blue. waveColor := ColorValue purple. waveThickness := 1. samples := self intensitySamples. samplingFreq := self samplingFrequency. "self assert: [samplingFreq + 1 = samples size]." samplesRotateOffset := (samplingFreq * phaseOffset uniformed deg / 360) rounded. rotatedSamples := self rotateIntensitySamples: samples openingIndexAt: 1 + samplesRotateOffset. revolutionDirection sign < 0 ifTrue: [rotatedSamples := rotatedSamples reverse]. chartData := rotatedSamples collect: [:intensity | Array with: intensity]. chart := JunChartLine sample: chartData. chart valueRangeFrom: 0 to: 2. chart axisColor: axisColor. chart colors: (Array with: waveColor). chart lineWidth: waveThickness. chart showXAxis. chart showYAxis. chart fit. ^chart asImageExtent: extent depthAt: phase ^self subclassResponsibility showExtent: extent | showImage model | showImage := self showImageExtent: extent. model := JunImageDisplayModel show: showImage label: 'Katachi and its wave'. ^model compare: compareBlock samples: baseSamples with: objectSamples indexShift: indexShift | compareSize rotatedObjectSamples | compareSize := self samplingFrequency. indexShift = 0 ifTrue: [rotatedObjectSamples := objectSamples] ifFalse: [rotatedObjectSamples := self rotateIntensitySamples: objectSamples openingIndexAt: compareSize + 1 - indexShift]. 1 to: compareSize do: [:index | compareBlock value: (baseSamples at: index) value: (rotatedObjectSamples at: index)] " | compareSize | compareSize := self samplingFrequency. 1 to: compareSize do: [:index | compareBlock value: (baseSamples at: index) value: (objectSamples at: (index - 1 - indexShift) \\ compareSize + 1)] " figureImageExtent: extent rotatePhase: rotatePhase | rotateAngle figureColor figureImage scale mecCenter imageCenter fitToImage mecRepresentation figureRepresentation tMoveToZero tResize tRotatePhaseOffset tAdaptImageCoordinate tMoveToImageCenter | rotateAngle := JunAngle fromDeg: 360 * rotatePhase. figureColor := ColorValue purple. figureImage := JunImageUtility imageExtent: extent. scale := (extent x min: extent y) / minimalEnclosingCircle radius / 2. mecCenter := minimalEnclosingCircle center. imageCenter := extent / 2. tMoveToZero := Jun2dTransformation translate: mecCenter negated. tResize := Jun2dTransformation scale: scale. tRotatePhaseOffset := (Jun2dTransformation rotate: rotateAngle). tAdaptImageCoordinate := Jun2dTransformation mirrorY. tMoveToImageCenter := Jun2dTransformation translate: imageCenter. fitToImage := (((tMoveToZero product: tResize) product: tRotatePhaseOffset) product: tAdaptImageCoordinate) product: tMoveToImageCenter. figureRepresentation := figure transform: fitToImage. mecRepresentation := minimalEnclosingCircle transform: fitToImage. figureImage := JunImageUtility imageExtent: extent displayBlock: [:gc | (Circle center: mecRepresentation center radius: mecRepresentation radius) displayStrokedOn: gc. gc paint: figureColor. figureRepresentation displayOn: gc. self displayOpeningMarkerOn: gc center: mecRepresentation center radius: mecRepresentation radius rotatePhase: rotatePhase]. ^figureImage figureImageExtent: extent ^self figureImageExtent: extent rotatePhase: 0 rotateIntensitySamples: sampleCollection openingIndexAt: openingIndex | size rotatedCollection latterHalfSize frequency | openingIndex = 1 ifTrue: [^sampleCollection copy]. size := sampleCollection size. frequency := size - 1. rotatedCollection := sampleCollection species withSize: size. latterHalfSize := frequency - (openingIndex - 1). rotatedCollection replaceFrom: 1 to: latterHalfSize with: sampleCollection startingAt: openingIndex. rotatedCollection replaceFrom: latterHalfSize + 1 to: frequency with: sampleCollection startingAt: 1. rotatedCollection at: size put: (rotatedCollection at: 1). ^rotatedCollection displayOpeningMarkerOn: graphicsContext center: center radius: radius rotatePhase: rotatePhase | gc centerPoint openingMarkColor openingMarkThickness directionArrowRemoteness rotateAngle farPoint directionArrowOrigin directionArrowTip arrowOpenness head1Angle head2Angle | openingMarkColor := ColorValue blue. openingMarkThickness := 1. directionArrowRemoteness := 0.7. gc := graphicsContext copy. centerPoint := center asPoint. gc translateBy: centerPoint. rotateAngle := Double pi * 2 * rotatePhase. farPoint := rotateAngle sin negated @ rotateAngle cos negated * radius. gc paint: openingMarkColor. gc lineWidth: openingMarkThickness. gc displayLineFrom: Point zero to: farPoint. directionArrowOrigin := farPoint * directionArrowRemoteness. gc translateBy: directionArrowOrigin. directionArrowTip := ((rotateAngle cos negated) @ (rotateAngle sin)) * 10. gc displayLineFrom: Point zero to: directionArrowTip. gc translateBy: directionArrowTip. arrowOpenness := 45 degreesToRadians. head1Angle := rotateAngle + (arrowOpenness / 2). head2Angle := rotateAngle - (arrowOpenness / 2). gc displayLineFrom: Point zero to: (head1Angle cos @ head1Angle sin negated) * 7. gc displayLineFrom: Point zero to: (head2Angle cos @ head2Angle sin negated) * 7 Katachi2dCircleWave class instance creation new ^super new initialize Katachi2dCircleWave initialize-release initialize super initialize. figure := Jun2dCircle center: 0, 0 radius: 1. minimalEnclosingCircle := Jun2dCircle center: 0, 0 radius: 1. ^self Katachi2dCircleWave accessing depthAt: phase ^0 Katachi2dCircleWave private figureImageExtent: extent rotatePhase: rotatePhase | figureColor figureImage mecCenter mecRepresentation figureRepresentation mecRadius | figureColor := ColorValue purple. mecCenter := extent / 2. mecRadius := (extent x min: extent y) / 2. figureImage := JunImageUtility imageExtent: extent displayBlock: [:gc | mecRepresentation := Circle center: mecCenter radius: mecRadius. figureRepresentation := Circle center: mecCenter radius: mecRadius. mecRepresentation displayStrokedOn: gc. gc paint: figureColor. figureRepresentation displayFilledOn: gc. self displayOpeningMarkerOn: gc center: mecCenter radius: mecRadius rotatePhase: rotatePhase]. ^figureImage Katachi2dReverseCircleWave class instance creation new "Return a wave representing a virtual figure whose depth from M.E.C. is always 2." ^super new initialize Katachi2dReverseCircleWave initialize-release initialize super initialize. figure := Jun2dCircle center: 0, 0 radius: -1. minimalEnclosingCircle := Jun2dCircle center: 0, 0 radius: 1. ^self Katachi2dReverseCircleWave accessing depthAt: phase ^2 Katachi2dReverseCircleWave private figureImageExtent: extent rotatePhase: rotatePhase | figureColor figureImage mecCenter mecRepresentation mecRadius figureCenter figureRadius prickleRadius theta direction prickles arrowOpenness prickleOrigin prickleTip head1Angle head2Angle | figureColor := ColorValue purple. mecCenter := extent / 2. mecRadius := (extent x min: extent y) / 2. figureImage := JunImageUtility imageExtent: extent displayBlock: [:gc | mecRepresentation := Circle center: mecCenter radius: mecRadius. mecRepresentation displayStrokedOn: gc. gc paint: figureColor. figureCenter := mecCenter. figureRadius := mecRadius - 1. prickleRadius := figureRadius * 0.9. (Circle center: figureCenter radius: figureRadius) displayStrokedOn: gc. prickles := 20. arrowOpenness := 30 degreesToRadians. 0 to: prickles - 1 do: [:tick | theta := tick / prickles * Double pi * 2. direction := theta cos @ theta sin. prickleOrigin := direction * figureRadius + figureCenter. prickleTip := direction * prickleRadius + figureCenter. gc displayLineFrom: prickleOrigin to: prickleTip. head1Angle := theta + (arrowOpenness / 2). head2Angle := theta - (arrowOpenness / 2). gc displayLineFrom: prickleTip to: (head1Angle cos @ head1Angle sin) * 5 + prickleTip. gc displayLineFrom: prickleTip to: (head2Angle cos @ head2Angle sin) * 5 + prickleTip]. self displayOpeningMarkerOn: gc center: mecCenter radius: mecRadius rotatePhase: rotatePhase]. ^figureImage Katachi2dNullWave class instance creation new "Return a wave representing a virtual figure whose depth from M.E.C. is always 0." ^super new initialize Katachi2dNullWave initialize-release initialize super initialize. figure := Jun2dCircle center: 0, 0 radius: 0. minimalEnclosingCircle := Jun2dCircle center: 0, 0 radius: 1. ^self Katachi2dNullWave accessing depthAt: phase ^1 Katachi2dNullWave private figureImageExtent: extent rotatePhase: rotatePhase | figureColor figureImage mecCenter mecRepresentation figureRepresentation mecRadius nullFigureSize | nullFigureSize := 3. figureColor := ColorValue purple. mecCenter := extent / 2. mecRadius := (extent x min: extent y) / 2. figureImage := JunImageUtility imageExtent: extent displayBlock: [:gc | mecRepresentation := Circle center: mecCenter radius: mecRadius. figureRepresentation := Circle center: mecCenter radius: nullFigureSize. mecRepresentation displayStrokedOn: gc. gc paint: figureColor. figureRepresentation displayFilledOn: gc. self displayOpeningMarkerOn: gc center: mecCenter radius: mecRadius rotatePhase: rotatePhase]. ^figureImage Katachi2dHalfCircumferenceWave class instance creation new "Return a wave representing the real figure that have the most biggest average depth in all of real possible figures." ^super new initialize Katachi2dHalfCircumferenceWave initialize-release initialize | division arcOuter arcInner theta thickness | super initialize. division := 64. thickness := 0.001d. arcOuter := Array new: division + 1. arcInner := Array new: division + 1. 0 to: division do: [:i | theta := (Double pi * 2) * i / division * 0.5d. arcOuter at: i + 1 put: theta cos @ theta sin. arcInner at: i + 1 put: (theta cos negated @ theta sin) * (1 - thickness)]. figure := Jun2dPolygon points: arcOuter, arcInner. minimalEnclosingCircle := Jun2dCircle center: 0, 0 radius: 1. ^self Katachi2dHalfCircumferenceWave accessing depthAt: phase phase <= 0.5 ifTrue: [^0] ifFalse:[^2] Katachi2dHalfCircumferenceWave private figureImageExtent: extent rotatePhase: rotatePhase | figureColor figureImage mecCenter mecRepresentation figureRepresentation mecRadius thickness | figureColor := ColorValue purple. thickness := 2. mecCenter := extent / 2. mecRadius := (extent x min: extent y) / 2. figureImage := JunImageUtility imageExtent: extent displayBlock: [:gc | mecRepresentation := Circle center: mecCenter radius: mecRadius. figureRepresentation := EllipticalArc boundingBox: mecRepresentation bounds startAngle: 180 sweepAngle: 180. mecRepresentation displayStrokedOn: gc. gc paint: figureColor. gc lineWidth: thickness. figureRepresentation displayStrokedOn: gc. self displayOpeningMarkerOn: gc center: mecCenter radius: mecRadius rotatePhase: rotatePhase]. ^figureImage Katachi2dPolygonWave class examples exampleButterfly "Katachi2dPolygonWave exampleButterfly show" | vertexes butterfly | vertexes := OrderedCollection new. vertexes add: 1, 1. vertexes add: 0, 0.1. vertexes add: -1, 1. vertexes add: -1, -1. vertexes add: 0, -0.1. vertexes add: 1, -1. butterfly := Jun2dPolygon vertexes: vertexes asArray. ^Katachi2dPolygonWave fromPolygon: butterfly. exampleBar "A long rectangle resembling a bar." "Katachi2dPolygonWave exampleBar show" | vertexes bar | vertexes := OrderedCollection new. vertexes add: 0, 0. vertexes add: 10, 0. vertexes add: 10, 1. vertexes add: 0, 1. bar := Jun2dPolygon vertexes: vertexes asArray. ^Katachi2dPolygonWave fromPolygon: bar. exampleRightIsoscelesTriangle1 "A right isosceles triangle. Mirror image of exampleRightIsoscelesTriangle2" "Katachi2dPolygonWave exampleRightIsoscelesTriangle1 show" | vertexes triangle | vertexes := OrderedCollection new. vertexes add: 0, 0. vertexes add: 1, 0. vertexes add: 0, 1. triangle := Jun2dPolygon vertexes: vertexes asArray. ^Katachi2dPolygonWave fromPolygon: triangle. exampleDeceivingCup "A raised bottom cup." "Katachi2dPolygonWave exampleDeceivingCup show" "Katachi2dWave similarityMatrix: (Array with: Katachi2dPolygonWave exampleDeceivingCup with: Katachi2dPolygonWave exampleCup)" | vertexes cup | vertexes := OrderedCollection new. vertexes add: 0, 0. vertexes add: 100, 0. vertexes add: 100, 100. vertexes add: 90, 100. vertexes add: 55, 50. vertexes add: 82, 10. vertexes add: 18, 10. vertexes add: 45, 50. vertexes add: 10, 100. vertexes add: 0, 100. cup := Jun2dPolygon vertexes: vertexes asArray. ^Katachi2dPolygonWave fromPolygon: cup. exampleCup "Katachi2dPolygonWave exampleCup show" "Katachi2dWave similarityMatrix: (Array with: Katachi2dPolygonWave exampleDeceivingCup with: Katachi2dPolygonWave exampleCup)" | vertexes cup | vertexes := OrderedCollection new. vertexes add: 0, 0. vertexes add: 100, 0. vertexes add: 100, 100. vertexes add: 90, 100. vertexes add: 90, 10. vertexes add: 10, 10. vertexes add: 10, 100. vertexes add: 0, 100. cup := Jun2dPolygon vertexes: vertexes asArray. ^Katachi2dPolygonWave fromPolygon: cup. exampleShell "Katachi2dPolygonWave exampleShell show" | vertexes ticks theta r shell | ticks := 10. vertexes := OrderedCollection new. 0 to: ticks - 1 do: [:tick | theta := Double pi * 2 * tick / ticks. r := ticks - tick / ticks. vertexes add: (theta cos , theta sin) * r]. shell := Jun2dPolygon vertexes: vertexes asArray. ^Katachi2dPolygonWave fromPolygon: shell. exampleRightIsoscelesTriangle2 "A right isosceles triangle. Mirror image of exampleRightIsoscelesTriangle1" "Katachi2dPolygonWave exampleRightIsoscelesTriangle2 show" | vertexes triangle | vertexes := OrderedCollection new. vertexes add: 0, 0. vertexes add: -1, 0. vertexes add: 0, 1. triangle := Jun2dPolygon vertexes: vertexes asArray. ^Katachi2dPolygonWave fromPolygon: triangle. Katachi2dPolygonWave class instance creation fromPolygon: aJun2dPolygon | newwave | newwave := self new initialize. newwave setFigure: aJun2dPolygon. ^newwave stellatedPolygon: numberOfHorns "(Katachi2dPolygonWave stellatedPolygon: 13) show" | rightPolygonVertexes theta seed p1 p2 e1 e2 stellatedVertexes valleyRadius stellatedPolygon | numberOfHorns odd ifFalse: [^nil]. numberOfHorns < 5 ifTrue: [^nil]. rightPolygonVertexes := Array new: numberOfHorns. 1 to: numberOfHorns do: [:i | theta := i / numberOfHorns * Double pi * 2. rightPolygonVertexes at: i put: theta cos @ theta sin]. seed := numberOfHorns - 1 / 2. p1 := rightPolygonVertexes at: 1. p2 := rightPolygonVertexes at: seed + 1. e1 := Jun2dLine from: p1 to: p2. p1 := rightPolygonVertexes at: 2. p2 := rightPolygonVertexes at: seed + 3. e2 := Jun2dLine from: p1 to: p2. valleyRadius := (e1 intersectingPointWithLine: e2) rho. stellatedVertexes := Array new: numberOfHorns * 2. 1 to: numberOfHorns do: [:i | stellatedVertexes at: i * 2 - 1 put: (rightPolygonVertexes at: i). theta := i + 0.5d / numberOfHorns * Double pi * 2. stellatedVertexes at: i * 2 put: (theta cos @ theta sin) * valleyRadius]. stellatedPolygon := Jun2dPolygon points: stellatedVertexes. ^self fromPolygon: stellatedPolygon regularPolygon: numberOfVertexes "(Katachi2dPolygonWave regularPolygon: 3) show" | vertexes theta polygon centralAngle | numberOfVertexes < 3 ifTrue: [^nil]. vertexes := Array new: numberOfVertexes. centralAngle := Double pi * 2 / numberOfVertexes. 1 to: numberOfVertexes do: [:i | theta := centralAngle * i. vertexes at: i put: theta cos @ theta sin]. polygon := Jun2dPolygon points: vertexes. ^self fromPolygon: polygon Katachi2dPolygonWave private depthAt: phase | center radius viewPoint viewLine edge rawDepth sight sightRawDepth | center := minimalEnclosingCircle center. radius := minimalEnclosingCircle radius. viewPoint:= center + (Jun2dPoint rho: radius theta: 2 * Double pi * phase). viewLine := Jun2dLine from: viewPoint to: center. rawDepth := nil. figure edgesDo: [:point1 :point2 | edge := Jun2dLine from: point1 to: point2. sight := self edge: edge isVisibleOn: viewLine. (sight isNil and: [edge containsPoint: viewPoint]) ifTrue: [sight := edge center]. sight isNil ifFalse: [ sightRawDepth := (sight - viewPoint) length. rawDepth isNil ifTrue: [rawDepth := sightRawDepth] ifFalse: [rawDepth := rawDepth min: sightRawDepth]]]. ^rawDepth isNil ifTrue: [^2] ifFalse: [^rawDepth / radius] minimalEnclosingCircleOf: aJun2dPolygon | bbox offset delaunay delaunayPoints delaunayExtent delaunayMEC | bbox := aJun2dPolygon boundingBox. offset := bbox origin. delaunayPoints := aJun2dPolygon points collect: [:vertex | vertex asPoint - offset]. delaunayExtent := bbox extent ceiling. delaunay := JunDelaunay2dDiagram extent: delaunayExtent x asInteger @ delaunayExtent y asInteger. delaunay addAll: delaunayPoints. delaunayMEC := delaunay minimalEnclosingCircle. minimalEnclosingCircle := delaunayMEC translatedBy: offset. ^minimalEnclosingCircle interpretFigure: aJun2dPolygon self addEyeCatchers: aJun2dPolygon points. minimalEnclosingCircle := self minimalEnclosingCircleOf: aJun2dPolygon edge: segmentJun2dLine isVisibleOn: viewJun2dLine | intersectingT | intersectingT := segmentJun2dLine intersectingTWithLine: viewJun2dLine. intersectingT isNil ifTrue: [^nil]. intersectingT < 0 ifTrue: [^nil]. intersectingT > 1 ifTrue: [^nil]. ^segmentJun2dLine atT: intersectingT