From 7a30bfc3fbfcaa6bef363007a2e66aa0a86ad36d Mon Sep 17 00:00:00 2001 From: Brendan Date: Mon, 8 Dec 2025 14:30:59 +0100 Subject: [PATCH 1/6] Fix compute distance for segments (not lines) --- src/Geometry/Collection.extension.st | 8 +-- src/Geometry/G1DElement.class.st | 14 +++-- src/Geometry/G2DCoordinates.class.st | 12 ++-- src/Geometry/G3DCoordinates.class.st | 16 +++--- src/Geometry/GAngle.class.st | 76 +++++++++++++------------ src/Geometry/GArc.class.st | 48 ++++++++-------- src/Geometry/GCircle.class.st | 20 ++++--- src/Geometry/GCoordinates.class.st | 68 +++++++++++----------- src/Geometry/GElement.class.st | 38 +++++++------ src/Geometry/GEllipse.class.st | 66 ++++++++++----------- src/Geometry/GLine.class.st | 64 +++++++++++---------- src/Geometry/GMatrix.class.st | 24 ++++---- src/Geometry/GPoint.class.st | 58 ++++++++++--------- src/Geometry/GPolygon.class.st | 48 ++++++++-------- src/Geometry/GRay.class.st | 40 ++++++------- src/Geometry/GRectangle.class.st | 26 +++++---- src/Geometry/GSegment.class.st | 65 ++++++++++++--------- src/Geometry/GShape.class.st | 26 +++++---- src/Geometry/GTriangle.class.st | 24 ++++---- src/Geometry/GVector.class.st | 44 +++++++------- src/Geometry/GZeroVector.class.st | 14 +++-- src/Geometry/GeometryError.class.st | 8 ++- src/Geometry/GeometryObject.class.st | 16 +++--- src/Geometry/Number.extension.st | 20 +++---- src/Geometry/Point.extension.st | 6 +- src/Geometry/TGWithCoordinates.trait.st | 26 +++++---- src/Geometry/package.st | 2 +- 27 files changed, 467 insertions(+), 410 deletions(-) diff --git a/src/Geometry/Collection.extension.st b/src/Geometry/Collection.extension.st index b9c0136..3a4b6de 100644 --- a/src/Geometry/Collection.extension.st +++ b/src/Geometry/Collection.extension.st @@ -1,16 +1,16 @@ -Extension { #name : #Collection } +Extension { #name : 'Collection' } -{ #category : #'*Geometry' } +{ #category : '*Geometry' } Collection >> asGCoordinates [ ^ GCoordinates withCollection: self ] -{ #category : #'*Geometry' } +{ #category : '*Geometry' } Collection >> asGPoint [ ^ GPoint withCollection: self ] -{ #category : #'*Geometry' } +{ #category : '*Geometry' } Collection >> asGVector [ ^ GVector withCollection: self ] diff --git a/src/Geometry/G1DElement.class.st b/src/Geometry/G1DElement.class.st index 4a93294..25a0602 100644 --- a/src/Geometry/G1DElement.class.st +++ b/src/Geometry/G1DElement.class.st @@ -5,22 +5,24 @@ Description I am the common superclass to all 1D elements. A 1D elements covers elements such as points, lines, segments, rays... " Class { - #name : #G1DElement, - #superclass : #GElement, - #category : #'Geometry-Elements' + #name : 'G1DElement', + #superclass : 'GElement', + #category : 'Geometry-Elements', + #package : 'Geometry', + #tag : 'Elements' } -{ #category : #testing } +{ #category : 'testing' } G1DElement class >> isAbstract [ ^ self = G1DElement ] -{ #category : #testing } +{ #category : 'testing' } G1DElement >> boundaryContains: aPoint [ ^ self includes: aPoint ] -{ #category : #accessing } +{ #category : 'accessing' } G1DElement >> length [ "Return the length of the element." diff --git a/src/Geometry/G2DCoordinates.class.st b/src/Geometry/G2DCoordinates.class.st index 79929a6..b456dde 100644 --- a/src/Geometry/G2DCoordinates.class.st +++ b/src/Geometry/G2DCoordinates.class.st @@ -14,17 +14,19 @@ Examples G2DCoordinates x: 1 y: 2 " Class { - #name : #G2DCoordinates, - #superclass : #GCoordinates, - #category : #'Geometry-Core' + #name : 'G2DCoordinates', + #superclass : 'GCoordinates', + #category : 'Geometry-Core', + #package : 'Geometry', + #tag : 'Core' } -{ #category : #accessing } +{ #category : 'accessing' } G2DCoordinates class >> numberOfDimensions [ ^ 2 ] -{ #category : #'instance creation' } +{ #category : 'instance creation' } G2DCoordinates class >> x: aNumber y: anotherNumber [ ^ self new x: aNumber; diff --git a/src/Geometry/G3DCoordinates.class.st b/src/Geometry/G3DCoordinates.class.st index b007a2a..58add6c 100644 --- a/src/Geometry/G3DCoordinates.class.st +++ b/src/Geometry/G3DCoordinates.class.st @@ -14,17 +14,19 @@ Examples G2DCoordinates x: 1 y: 2 z: 3 " Class { - #name : #G3DCoordinates, - #superclass : #GCoordinates, - #category : #'Geometry-Core' + #name : 'G3DCoordinates', + #superclass : 'GCoordinates', + #category : 'Geometry-Core', + #package : 'Geometry', + #tag : 'Core' } -{ #category : #accessing } +{ #category : 'accessing' } G3DCoordinates class >> numberOfDimensions [ ^ 3 ] -{ #category : #'instance creation' } +{ #category : 'instance creation' } G3DCoordinates class >> x: aNumber y: anotherNumber z: thirdNumber [ ^ self new x: aNumber; @@ -33,12 +35,12 @@ G3DCoordinates class >> x: aNumber y: anotherNumber z: thirdNumber [ yourself ] -{ #category : #accessing } +{ #category : 'accessing' } G3DCoordinates >> z [ ^ coordinates at: 3 ] -{ #category : #accessing } +{ #category : 'accessing' } G3DCoordinates >> z: aNumber [ ^ coordinates at: 3 put: aNumber ] diff --git a/src/Geometry/GAngle.class.st b/src/Geometry/GAngle.class.st index feeccff..22feae8 100644 --- a/src/Geometry/GAngle.class.st +++ b/src/Geometry/GAngle.class.st @@ -45,8 +45,8 @@ Internal Representation and Key Implementation Points. " Class { - #name : #GAngle, - #superclass : #GeometryObject, + #name : 'GAngle', + #superclass : 'GeometryObject', #instVars : [ 'radians' ], @@ -56,15 +56,17 @@ Class { 'TwoPi', 'ZeroAngle' ], - #category : #'Geometry-Core' + #category : 'Geometry-Core', + #package : 'Geometry', + #tag : 'Core' } -{ #category : #'instance creation' } +{ #category : 'instance creation' } GAngle class >> degrees: anInteger [ ^ self radians: anInteger degreesToRadians ] -{ #category : #'class initialization' } +{ #category : 'class initialization' } GAngle class >> initialize [ TwoPi := 2π. ZeroAngle := 0 rads. @@ -72,49 +74,49 @@ GAngle class >> initialize [ StraightAngle := 1π rads ] -{ #category : #'instance creation' } +{ #category : 'instance creation' } GAngle class >> radians: aNumber [ ^ self new initializeWith: aNumber; yourself ] -{ #category : #'instance creation' } +{ #category : 'instance creation' } GAngle class >> rads: aNumber [ ^ self radians: aNumber ] -{ #category : #arithmetic } +{ #category : 'arithmetic' } GAngle >> * anInteger [ ^ anInteger multiplyWithAngle: self ] -{ #category : #arithmetic } +{ #category : 'arithmetic' } GAngle >> + aGAngle [ ^ aGAngle addWithAngle: self ] -{ #category : #arithmetic } +{ #category : 'arithmetic' } GAngle >> - aGAngle [ ^ aGAngle substractWithAngle: self ] -{ #category : #arithmetic } +{ #category : 'arithmetic' } GAngle >> / anInteger [ ^ anInteger divideWithAngle: self ] -{ #category : #comparison } +{ #category : 'comparison' } GAngle >> < aGAngle [ ^ self rads < aGAngle rads ] -{ #category : #comparison } +{ #category : 'comparison' } GAngle >> <= aGAngle [ ^ (self > aGAngle) not ] -{ #category : #comparing } +{ #category : 'comparing' } GAngle >> = anObject [ "Answer whether the receiver and anObject represent the same object." @@ -124,99 +126,99 @@ GAngle >> = anObject [ ^ (radians - anObject rads) =~ 0 or: [ (radians - anObject rads) =~ TwoPi ] ] -{ #category : #comparison } +{ #category : 'comparison' } GAngle >> > aGAngle [ ^ aGAngle < self ] -{ #category : #comparison } +{ #category : 'comparison' } GAngle >> >= aGAngle [ ^ aGAngle <= self ] -{ #category : #arithmetic } +{ #category : 'arithmetic' } GAngle >> adaptToNumber: anInteger andSend: aString [ (#(#/ #+) anySatisfy: [ :forgivenSelector | aString = forgivenSelector ]) ifTrue: [ self error: 'Cannot execute ' , aString , ' on an Integer with an angle' ]. "I think we can do better than an if here but no time." ^ self perform: aString with: anInteger ] -{ #category : #arithmetic } +{ #category : 'arithmetic' } GAngle >> addWithAngle: aGAngle [ ^ (self rads + aGAngle rads) rads ] -{ #category : #arithmetic } +{ #category : 'arithmetic' } GAngle >> cos [ ^ self rads cos ] -{ #category : #converting } +{ #category : 'converting' } GAngle >> degreeNumber [ ^ self rads radiansToDegrees ] -{ #category : #accessing } +{ #category : 'accessing' } GAngle >> explementary [ ^ (0 - self rads) rads ] -{ #category : #comparing } +{ #category : 'comparing' } GAngle >> hash [ "Answer an integer value that is related to the identity of the receiver." ^ radians hash ] -{ #category : #initialization } +{ #category : 'initialization' } GAngle >> initializeWith: aNumber [ radians := aNumber. radians := radians \\ TwoPi. radians negative ifTrue: [ radians := radians + TwoPi ] ] -{ #category : #testing } +{ #category : 'testing' } GAngle >> isAcute [ ^ self > 0 rads and: [ self < RightAngle ] ] -{ #category : #testing } +{ #category : 'testing' } GAngle >> isObtuse [ ^ self > RightAngle and: [ self < StraightAngle ] ] -{ #category : #testing } +{ #category : 'testing' } GAngle >> isReflex [ ^ self > StraightAngle and: [ self rads < TwoPi ] ] -{ #category : #testing } +{ #category : 'testing' } GAngle >> isRight [ ^ self = RightAngle ] -{ #category : #testing } +{ #category : 'testing' } GAngle >> isStraight [ ^ self = StraightAngle ] -{ #category : #testing } +{ #category : 'testing' } GAngle >> isZero [ ^ self = ZeroAngle ] -{ #category : #arithmetic } +{ #category : 'arithmetic' } GAngle >> multiplyWithAngle: aGAngle [ self error: 'Not possible to multiply an angle by another angle' ] -{ #category : #printing } +{ #category : 'printing' } GAngle >> printOn: aStream [ aStream nextPutAll: (self degreeNumber asString truncateTo: 10); nextPutAll: '°' ] -{ #category : #converting } +{ #category : 'converting' } GAngle >> radianNumber [ self @@ -225,7 +227,7 @@ GAngle >> radianNumber [ ^ self rads ] -{ #category : #accessing } +{ #category : 'accessing' } GAngle >> radians [ self @@ -234,22 +236,22 @@ GAngle >> radians [ ^ self rads ] -{ #category : #accessing } +{ #category : 'accessing' } GAngle >> rads [ ^ radians ] -{ #category : #arithmetic } +{ #category : 'arithmetic' } GAngle >> sin [ ^ self rads sin ] -{ #category : #arithmetic } +{ #category : 'arithmetic' } GAngle >> substractWithAngle: aGAngle [ ^ (aGAngle rads - self rads) rads ] -{ #category : #arithmetic } +{ #category : 'arithmetic' } GAngle >> tan [ ^ self rads tan ] diff --git a/src/Geometry/GArc.class.st b/src/Geometry/GArc.class.st index 958f768..895c2ef 100644 --- a/src/Geometry/GArc.class.st +++ b/src/Geometry/GArc.class.st @@ -40,17 +40,19 @@ Internal Representation and Key Implementation Points. " Class { - #name : #GArc, - #superclass : #G1DElement, + #name : 'GArc', + #superclass : 'G1DElement', #instVars : [ 'center', 'origin', 'angle' ], - #category : #'Geometry-Elements' + #category : 'Geometry-Elements', + #package : 'Geometry', + #tag : 'Elements' } -{ #category : #'instance creation' } +{ #category : 'instance creation' } GArc class >> center: aGPoint origin: anotherGPoint angle: aGAngle [ "Take three parameters to create a new arc: - A point representing the center of the arc (center of the circle whose bond contains the arc circle) @@ -64,7 +66,7 @@ GArc class >> center: aGPoint origin: anotherGPoint angle: aGAngle [ yourself ] -{ #category : #'instance creation' } +{ #category : 'instance creation' } GArc class >> center: aGPoint origin: anotherGPoint direction: yetAnotherGPoint [ "Take three parameters to create a new arc: - A point representing the center of the arc (center of the circle whose bond contains the arc circle) @@ -75,7 +77,7 @@ GArc class >> center: aGPoint origin: anotherGPoint direction: yetAnotherGPoint ^ self center: aGPoint origin: anotherGPoint angle: (aGPoint - anotherGPoint angleWith: aGPoint - yetAnotherGPoint) ] -{ #category : #comparing } +{ #category : 'comparing' } GArc >> = anObject [ "Answer whether the receiver and anObject represent the same object." @@ -85,48 +87,48 @@ GArc >> = anObject [ ^ origin = anObject origin and: [ center = anObject center and: [ angle = anObject angle ] ] ] -{ #category : #accessing } +{ #category : 'accessing' } GArc >> angle [ ^ angle ] -{ #category : #accessing } +{ #category : 'accessing' } GArc >> angle: anObject [ angle := anObject ] -{ #category : #accessing } +{ #category : 'accessing' } GArc >> center [ ^ center ] -{ #category : #accessing } +{ #category : 'accessing' } GArc >> center: anObject [ center := anObject ] -{ #category : #accessing } +{ #category : 'accessing' } GArc >> centralAngle [ "Alias of angle to cover the geometric names" ^ self angle ] -{ #category : #accessing } +{ #category : 'accessing' } GArc >> endPoint [ "The end point is the point at the end of the arc (opposed to the origin)." ^ self origin rotatedBy: self angle about: self center ] -{ #category : #comparing } +{ #category : 'comparing' } GArc >> hash [ "Answer an integer value that is related to the identity of the receiver." ^ origin hash bitXor: (center hash bitXor: angle hash) ] -{ #category : #testing } +{ #category : 'testing' } GArc >> includes: aGPoint [ "An arc includes a point in the angle between the vector defined by the origin and center and the vector defined by the point and the center is between 0 and the arc angle and if the distance of the point from the center is equals to the radius." @@ -134,37 +136,37 @@ GArc >> includes: aGPoint [ and: [ (aGPoint distanceTo: self center) =~ self radius ] ] -{ #category : #intersections } +{ #category : 'intersections' } GArc >> intersectionsWith: anElement [ ^ anElement intersectionsWithArc: self ] -{ #category : #intersections } +{ #category : 'intersections' } GArc >> intersectionsWithEllipse: aGEllipse [ ^ aGEllipse intersectionsWithArc: self ] -{ #category : #intersections } +{ #category : 'intersections' } GArc >> intersectionsWithLine: aGLine [ ^ aGLine intersectionsWithArc: self ] -{ #category : #accessing } +{ #category : 'accessing' } GArc >> length [ ^ self angle rads * self radius ] -{ #category : #accessing } +{ #category : 'accessing' } GArc >> origin [ ^ origin ] -{ #category : #accessing } +{ #category : 'accessing' } GArc >> origin: anObject [ origin := anObject ] -{ #category : #comparing } +{ #category : 'comparing' } GArc >> printOn: aStream [ super printOn: aStream. @@ -179,12 +181,12 @@ GArc >> printOn: aStream [ << $) ] -{ #category : #accessing } +{ #category : 'accessing' } GArc >> radius [ ^ (self origin - self center) length ] -{ #category : #comparing } +{ #category : 'comparing' } GArc >> translateBy: aGVector [ self center: self center + aGVector. self origin: self origin + aGVector diff --git a/src/Geometry/GCircle.class.st b/src/Geometry/GCircle.class.st index 66d4c5a..e46ab8e 100644 --- a/src/Geometry/GCircle.class.st +++ b/src/Geometry/GCircle.class.st @@ -5,37 +5,39 @@ a * (x^2) + a * (y^2) + d * x + e * y + f = 0 " Class { - #name : #GCircle, - #superclass : #GEllipse, - #category : #'Geometry-Shapes' + #name : 'GCircle', + #superclass : 'GEllipse', + #category : 'Geometry-Shapes', + #package : 'Geometry', + #tag : 'Shapes' } -{ #category : #'instance creation' } +{ #category : 'instance creation' } GCircle class >> center: aPoint1 radius: aNumber [ ^ self center: aPoint1 vertex: aPoint1 x , (aPoint1 y + aNumber) coVertex: aPoint1 x + aNumber , aPoint1 y ] -{ #category : #comparing } +{ #category : 'comparing' } GCircle >> boundaryContains: aPoint [ ^ self radius =~ (self center distanceTo: aPoint) ] -{ #category : #accessing } +{ #category : 'accessing' } GCircle >> perimeter [ ^ 2π * self radius ] -{ #category : #printing } +{ #category : 'printing' } GCircle >> printOn: aStream [ aStream nextPutAll: ('x² + y² = {1}²' format: {self radius}) ] -{ #category : #accessing } +{ #category : 'accessing' } GCircle >> radius [ ^ self semiMajorAxisLength ] -{ #category : #'public interface' } +{ #category : 'public interface' } GCircle >> upperPoint [ ^ center x , ( center y + self radius ) ] diff --git a/src/Geometry/GCoordinates.class.st b/src/Geometry/GCoordinates.class.st index da676d9..ad200be 100644 --- a/src/Geometry/GCoordinates.class.st +++ b/src/Geometry/GCoordinates.class.st @@ -33,27 +33,29 @@ Internal Representation and Key Implementation Points. " Class { - #name : #GCoordinates, - #superclass : #GeometryObject, + #name : 'GCoordinates', + #superclass : 'GeometryObject', #instVars : [ 'coordinates' ], - #category : #'Geometry-Core' + #category : 'Geometry-Core', + #package : 'Geometry', + #tag : 'Core' } -{ #category : #'instance creation' } +{ #category : 'instance creation' } GCoordinates class >> newWithCoordinates: aCollection [ ^ self basicNew initializeWith: aCollection; yourself ] -{ #category : #accessing } +{ #category : 'accessing' } GCoordinates class >> numberOfDimensions [ ^ self subclassResponsibility ] -{ #category : #'instance creation' } +{ #category : 'instance creation' } GCoordinates class >> withCollection: aCollection [ ^ self allSubclasses detect: [ :e | e numberOfDimensions = aCollection size ] @@ -64,7 +66,7 @@ GCoordinates class >> withCollection: aCollection [ ifNone: [ self error: 'This amount of coordinated is not yet managed.' ] ] -{ #category : #arithmetic } +{ #category : 'arithmetic' } GCoordinates >> * aNumber [ "Answer a Point that is the multiplication of the receiver and arg." @@ -73,7 +75,7 @@ GCoordinates >> * aNumber [ ^ self class newWithCoordinates: (self coordinatesCollect: [ :number | number * aNumber ]) ] -{ #category : #arithmetic } +{ #category : 'arithmetic' } GCoordinates >> + otherCoordinates [ "Answer a Point that is the sum of the receiver and arg." self numberOfDimensions = otherCoordinates numberOfDimensions ifFalse: [ self error: 'For now we accept only addition of coordinates from the same dimension. We will see later if we should do more.' ]. @@ -81,7 +83,7 @@ GCoordinates >> + otherCoordinates [ ^ self class newWithCoordinates: (otherCoordinates coordinatesWith: coordinates collect: [ :number1 :number2 | number1 + number2 ]) ] -{ #category : #arithmetic } +{ #category : 'arithmetic' } GCoordinates >> - otherCoordinates [ "Answer a Point that is the substraction of the receiver and arg." self numberOfDimensions = otherCoordinates numberOfDimensions ifFalse: [ self error: 'For now we accept only substraction of coordinates from the same dimension. We will see later if we should do more.' ]. @@ -89,7 +91,7 @@ GCoordinates >> - otherCoordinates [ ^ self class newWithCoordinates: (otherCoordinates coordinatesWith: coordinates collect: [ :number2 :number1 | number1 - number2 ]) ] -{ #category : #arithmetic } +{ #category : 'arithmetic' } GCoordinates >> / aNumber [ "Answer a Point that is the division of the receiver and arg." @@ -98,7 +100,7 @@ GCoordinates >> / aNumber [ ^ self class newWithCoordinates: (self coordinatesCollect: [ :number | number / aNumber ]) ] -{ #category : #comparing } +{ #category : 'comparing' } GCoordinates >> = otherCoordinates [ "Answer whether the receiver and anObject represent the same object." @@ -110,122 +112,122 @@ GCoordinates >> = otherCoordinates [ ^ true ] -{ #category : #converting } +{ #category : 'converting' } GCoordinates >> asGPoint [ ^ GPoint coordinates: self ] -{ #category : #converting } +{ #category : 'converting' } GCoordinates >> asGVector [ ^ GVector coordinates: self ] -{ #category : #accessing } +{ #category : 'accessing' } GCoordinates >> at: anInteger [ ^ self coordinates at: anInteger ] -{ #category : #accessing } +{ #category : 'accessing' } GCoordinates >> coordinates [ ^ coordinates ] -{ #category : #enumerating } +{ #category : 'enumerating' } GCoordinates >> coordinatesCollect: aBlock [ ^ coordinates collect: aBlock ] -{ #category : #enumerating } +{ #category : 'enumerating' } GCoordinates >> coordinatesWith: aCollection collect: aBlock [ ^ coordinates with: aCollection collect: aBlock ] -{ #category : #enumerating } +{ #category : 'enumerating' } GCoordinates >> coordinatesWith: aCollection do: aBlock [ coordinates with: aCollection do: aBlock ] -{ #category : #enumerating } +{ #category : 'enumerating' } GCoordinates >> fold: aBlock [ ^ self coordinates fold: aBlock ] -{ #category : #comparing } +{ #category : 'comparing' } GCoordinates >> hash [ "Answer an integer value that is related to the identity of the receiver." ^ coordinates hash negated "We negate to not have a collision with Array" ] -{ #category : #initialization } +{ #category : 'initialization' } GCoordinates >> initialize [ super initialize. coordinates := Array new: self numberOfDimensions ] -{ #category : #private } +{ #category : 'private' } GCoordinates >> initializeWith: anArray [ anArray size = self numberOfDimensions ifFalse: [ self error: 'The provided array must have the same size than my number of dimensions which is ' , self numberOfDimensions size asString ]. coordinates := anArray ] -{ #category : #enumerating } +{ #category : 'enumerating' } GCoordinates >> inject: aSeed into: aBlock [ ^ self coordinates inject: aSeed into: aBlock ] -{ #category : #accessing } +{ #category : 'accessing' } GCoordinates >> lowerLimitsWith: otherCoordinates [ ^ self class newWithCoordinates: (self coordinatesWith: otherCoordinates coordinates collect: [ :number1 :number2 | number1 min: number2 ]) ] -{ #category : #accessing } +{ #category : 'accessing' } GCoordinates >> numberOfDimensions [ ^ self class numberOfDimensions ] -{ #category : #copying } +{ #category : 'copying' } GCoordinates >> postCopy [ super postCopy. coordinates := coordinates copy ] -{ #category : #printing } +{ #category : 'printing' } GCoordinates >> printCoordinatesOn: aStream [ aStream << $(. coordinates do: [ :number | aStream << number asString ] separatedBy: [ aStream << $, ]. aStream << $) ] -{ #category : #printing } +{ #category : 'printing' } GCoordinates >> printOn: aStream [ self printCoordinatesOn: aStream ] -{ #category : #accessing } +{ #category : 'accessing' } GCoordinates >> upperLimitsWith: otherCoordinates [ ^ self class newWithCoordinates: (self coordinatesWith: otherCoordinates coordinates collect: [ :number1 :number2 | number1 max: number2 ]) ] -{ #category : #accessing } +{ #category : 'accessing' } GCoordinates >> x [ ^ coordinates at: 1 ] -{ #category : #accessing } +{ #category : 'accessing' } GCoordinates >> x: aNumber [ coordinates at: 1 put: aNumber ] -{ #category : #accessing } +{ #category : 'accessing' } GCoordinates >> y [ "Might return an error for 1D coordinates, but do they make sense?" ^ coordinates at: 2 ] -{ #category : #accessing } +{ #category : 'accessing' } GCoordinates >> y: aNumber [ "Might return an error for 1D coordinates, but do they make sense?" diff --git a/src/Geometry/GElement.class.st b/src/Geometry/GElement.class.st index 8f802dc..8d1d5be 100644 --- a/src/Geometry/GElement.class.st +++ b/src/Geometry/GElement.class.st @@ -16,17 +16,19 @@ Public API and Key Messages " Class { - #name : #GElement, - #superclass : #GeometryObject, - #category : #'Geometry-Elements' + #name : 'GElement', + #superclass : 'GeometryObject', + #category : 'Geometry-Elements', + #package : 'Geometry', + #tag : 'Elements' } -{ #category : #testing } +{ #category : 'testing' } GElement class >> isAbstract [ ^ self = GElement ] -{ #category : #testing } +{ #category : 'testing' } GElement >> boundaryContains: aPoint [ "Should return true if the boundaries of the element contains the point. Else it should return false. @@ -35,17 +37,17 @@ GElement >> boundaryContains: aPoint [ ^ self subclassResponsibility ] -{ #category : #testing } +{ #category : 'testing' } GElement >> boundaryContainsAny: points [ ^ points anySatisfy: [ :point | self boundaryContains: point ] ] -{ #category : #testing } +{ #category : 'testing' } GElement >> boundaryContainsWhichOf: points [ ^ points select: [ :point | self boundaryContains: point ] ] -{ #category : #testing } +{ #category : 'testing' } GElement >> contains: aPoint [ "Return true if the element includes the argument but not in its boundaries. @@ -55,7 +57,7 @@ GElement >> contains: aPoint [ ^ (self includes: aPoint) and: [ (self boundaryContains: aPoint) not ] ] -{ #category : #testing } +{ #category : 'testing' } GElement >> includes: aPoint [ "I should return true if the points is contained in the element including the boundaries. Else I will answer false." @@ -63,53 +65,53 @@ GElement >> includes: aPoint [ ^ self subclassResponsibility ] -{ #category : #intersections } +{ #category : 'intersections' } GElement >> intersectionsWith: anElement [ "We should use double dispatch to find intersection points" ^ self subclassResponsibility ] -{ #category : #intersections } +{ #category : 'intersections' } GElement >> intersectionsWithArc: anArc [ ^ (self intersectionsWithEllipse: (GCircle center: anArc center radius: anArc radius)) select: [ :point | anArc includes: point ] ] -{ #category : #intersections } +{ #category : 'intersections' } GElement >> intersectionsWithEllipse: aGEllipse [ "I should return the intersection points between me and an ellipse" ^ self subclassResponsibility ] -{ #category : #intersections } +{ #category : 'intersections' } GElement >> intersectionsWithLine: aGLine [ "I should return the intersection points between me and a line" ^ self subclassResponsibility ] -{ #category : #intersections } +{ #category : 'intersections' } GElement >> intersectionsWithPoint: aPoint [ ^ (self boundaryContains: aPoint) ifTrue: [ {aPoint} ] ifFalse: [ {} ] ] -{ #category : #intersections } +{ #category : 'intersections' } GElement >> intersectionsWithPolygon: aGPolygon [ ^ (aGPolygon edges flatCollect: [ :segment | segment intersectionsWith: self ]) asSet ] -{ #category : #intersections } +{ #category : 'intersections' } GElement >> intersectionsWithRay: aGRay [ ^ (aGRay asGLine intersectionsWith: self) select: [ :point | aGRay includes: point ] ] -{ #category : #intersections } +{ #category : 'intersections' } GElement >> intersectionsWithSegment: aGSegment [ ^ (aGSegment asGLine intersectionsWith: self) select: [ :point | aGSegment includes: point ] ] -{ #category : #transforming } +{ #category : 'transforming' } GElement >> translateBy: aPoint [ "Translate the shape by a delta defined by a point." diff --git a/src/Geometry/GEllipse.class.st b/src/Geometry/GEllipse.class.st index afb8b88..e46a62a 100644 --- a/src/Geometry/GEllipse.class.st +++ b/src/Geometry/GEllipse.class.st @@ -9,17 +9,19 @@ GEllipse center: 0@0 vertex: 0@10 coVertex: 10@0 ``` " Class { - #name : #GEllipse, - #superclass : #GShape, + #name : 'GEllipse', + #superclass : 'GShape', #instVars : [ 'center', 'vertex', 'coVertex' ], - #category : #'Geometry-Shapes' + #category : 'Geometry-Shapes', + #package : 'Geometry', + #tag : 'Shapes' } -{ #category : #'instance creation' } +{ #category : 'instance creation' } GEllipse class >> center: aGPoint vertex: aGPoint2 coVertex: aGPoint3 [ | d1 d2 zero | d1 := aGPoint - aGPoint2. @@ -36,7 +38,7 @@ GEllipse class >> center: aGPoint vertex: aGPoint2 coVertex: aGPoint3 [ yourself ] -{ #category : #comparing } +{ #category : 'comparing' } GEllipse >> = anObject [ "Answer whether the receiver and anObject represent the same object." @@ -46,48 +48,48 @@ GEllipse >> = anObject [ ^ vertex =~ anObject vertex and: [ center =~ anObject center and: [ coVertex =~ anObject coVertex ] ] ] -{ #category : #accessing } +{ #category : 'accessing' } GEllipse >> area [ "pi * a * b" ^ (self semiMajorAxisLength * self semiMinorAxisLength)π ] -{ #category : #testing } +{ #category : 'testing' } GEllipse >> boundaryContains: aPoint [ "Solution from: https://math.stackexchange.com/questions/76457/check-if-a-point-is-within-an-ellipse" ^ (aPoint x - center x) squared / self semiMajorAxisLength squared + ((aPoint y - center y) squared / self semiMinorAxisLength squared) = 1 ] -{ #category : #accessing } +{ #category : 'accessing' } GEllipse >> center [ ^ center ] -{ #category : #accessing } +{ #category : 'accessing' } GEllipse >> center: anObject [ center := anObject ] -{ #category : #accessing } +{ #category : 'accessing' } GEllipse >> coVertex [ ^ coVertex ] -{ #category : #accessing } +{ #category : 'accessing' } GEllipse >> coVertex: anObject [ coVertex := anObject ] -{ #category : #accessing } +{ #category : 'accessing' } GEllipse >> encompassingRectangle [ | radiuxPoints | radiuxPoints := { vertex . (center + (center - vertex)). coVertex . (center + (center - coVertex)) } collect: #coordinates. ^ GRectangle origin: (radiuxPoints fold: [ :pt1 :pt2 | pt1 upperLimitsWith: pt2 ]) asGPoint corner: (radiuxPoints fold: [ :pt1 :pt2 | pt1 lowerLimitsWith: pt2 ]) asGPoint ] -{ #category : #transforming } +{ #category : 'transforming' } GEllipse >> fitInExtent: extent [ | scales | scales := extent coordinatesWith: self encompassingRectangle extent coordinates collect: [ :number1 :number2 | number1 / number2 ]. @@ -95,45 +97,45 @@ GEllipse >> fitInExtent: extent [ coVertex := ((coVertex - center) coordinates coordinatesWith: scales collect: [ :a :b | a * b ]) asGVector + center ] -{ #category : #accessing } +{ #category : 'accessing' } GEllipse >> foci [ ^ self majorAxis asGLine intersectionsWith: (GCircle center: center radius: self fociLocation) ] -{ #category : #accessing } +{ #category : 'accessing' } GEllipse >> fociLocation [ "c² = a² - b²" ^ (self semiMajorAxisLength squared - self semiMinorAxisLength squared) sqrt ] -{ #category : #comparing } +{ #category : 'comparing' } GEllipse >> hash [ "Answer an integer value that is related to the identity of the receiver." ^ vertex hash bitXor: (center hash bitXor: coVertex hash) ] -{ #category : #testing } +{ #category : 'testing' } GEllipse >> includes: aPoint [ "Solution from: https://math.stackexchange.com/questions/76457/check-if-a-point-is-within-an-ellipse" ^ (aPoint x - center x) squared / self semiMajorAxisLength squared + ((aPoint y - center y) squared / self semiMinorAxisLength squared) <= 1 ] -{ #category : #intersections } +{ #category : 'intersections' } GEllipse >> intersectionsWith: anElement [ ^ anElement intersectionsWithEllipse: self ] -{ #category : #intersections } +{ #category : 'intersections' } GEllipse >> intersectionsWithEllipse: aGEllipse [ "And also tested" self shouldBeImplemented ] -{ #category : #intersections } +{ #category : 'intersections' } GEllipse >> intersectionsWithLine: aGLine [ " http://www.ambrsoft.com/TrigoCalc/Circles2/Ellipse/EllipseLine.htm @@ -177,7 +179,7 @@ GEllipse >> intersectionsWithLine: aGLine [ ^ {(x1 , y1) . (x2 , y2)} asSet ] -{ #category : #intersections } +{ #category : 'intersections' } GEllipse >> intersectionsWithVerticalLine: aGLine [ " Ellipse: ((x-h)²/a²) + ((y - k)²/b²) = 1 @@ -204,27 +206,27 @@ GEllipse >> intersectionsWithVerticalLine: aGLine [ ^ {(x , y1) . (x , y2)} asSet ] -{ #category : #accessing } +{ #category : 'accessing' } GEllipse >> majorAxis [ ^ GSegment with: center + (center - vertex) with: center + (vertex - center) ] -{ #category : #accessing } +{ #category : 'accessing' } GEllipse >> majorAxisLength [ ^ self majorAxis length ] -{ #category : #accessing } +{ #category : 'accessing' } GEllipse >> minorAxis [ ^ GSegment with: center + (center - coVertex) with: center + (coVertex - center) ] -{ #category : #accessing } +{ #category : 'accessing' } GEllipse >> minorAxisLength [ ^ self minorAxis length ] -{ #category : #accessing } +{ #category : 'accessing' } GEllipse >> perimeter [ "The perimeter of an ellipse cannot be calculated easily. This method will give an approximation with Ramanujan II" @@ -235,34 +237,34 @@ GEllipse >> perimeter [ ^ (semiMajorAndMinorLength * (1 + (3 * h / (10 + (4 - (3 * h)) squared))))π ] -{ #category : #printing } +{ #category : 'printing' } GEllipse >> printOn: aStream [ aStream nextPutAll: ('((x - {1})²/{2}) + ((y - {3})²/{4}) = 1' format: {center x . self semiMajorAxisLength squared . center y . self semiMinorAxisLength squared}) ] -{ #category : #accessing } +{ #category : 'accessing' } GEllipse >> semiMajorAxisLength [ ^ self majorAxis length / 2 ] -{ #category : #accessing } +{ #category : 'accessing' } GEllipse >> semiMinorAxisLength [ ^ self minorAxis length / 2 ] -{ #category : #transforming } +{ #category : 'transforming' } GEllipse >> translateBy: aGVector [ center translateBy: aGVector. vertex translateBy: aGVector. coVertex translateBy: aGVector ] -{ #category : #accessing } +{ #category : 'accessing' } GEllipse >> vertex [ ^ vertex ] -{ #category : #accessing } +{ #category : 'accessing' } GEllipse >> vertex: anObject [ vertex := anObject ] diff --git a/src/Geometry/GLine.class.st b/src/Geometry/GLine.class.st index 6091487..2904c92 100644 --- a/src/Geometry/GLine.class.st +++ b/src/Geometry/GLine.class.st @@ -27,17 +27,19 @@ Internal Representation and Key Implementation Points. " Class { - #name : #GLine, - #superclass : #G1DElement, + #name : 'GLine', + #superclass : 'G1DElement', #instVars : [ 'v1', 'v2', 'equationCache' ], - #category : #'Geometry-Elements' + #category : 'Geometry-Elements', + #package : 'Geometry', + #tag : 'Elements' } -{ #category : #'instance creation' } +{ #category : 'instance creation' } GLine class >> a: a b: b c: c [ "ax + by + c = 0 ax + c = -by @@ -51,7 +53,7 @@ GLine class >> a: a b: b c: c [ ^ b = 0 ifFalse: [ self through: 1 , ((a * 1 + c) / b) negated and: 2 , ((a * 2 + c) / b) negated ] ifTrue: [ self through: ((b * 1 + c) / a) negated , 1 and: ((b * 2 + c) / a) negated , 2 ] ] -{ #category : #'instance creation' } +{ #category : 'instance creation' } GLine class >> through: aPoint1 and: aPoint2 [ ^ self new v1: aPoint1; @@ -59,7 +61,7 @@ GLine class >> through: aPoint1 and: aPoint2 [ yourself ] -{ #category : #comparing } +{ #category : 'comparing' } GLine >> = line [ self == line ifTrue: [ ^ true ]. self class = line class ifFalse: [ ^ false ]. @@ -71,32 +73,32 @@ GLine >> = line [ do: [ "This can happen if b = 0" (self xFor: 1) =~ (line xFor: 1) ] ] -{ #category : #accessing } +{ #category : 'accessing' } GLine >> a [ ^ self linearEquation at: #a ] -{ #category : #arithmetic } +{ #category : 'arithmetic' } GLine >> angleWith: aLine [ ^ self v1 - self v2 angleWith: aLine v1 - aLine v2 ] -{ #category : #converting } +{ #category : 'converting' } GLine >> asGLine [ ^ self ] -{ #category : #accessing } +{ #category : 'accessing' } GLine >> b [ ^ self linearEquation at: #b ] -{ #category : #accessing } +{ #category : 'accessing' } GLine >> c [ ^ self linearEquation at: #c ] -{ #category : #accessing } +{ #category : 'accessing' } GLine >> determinantWith: aLine [ "I return the determinant between two line. @@ -109,34 +111,34 @@ GLine >> determinantWith: aLine [ ^ (GMatrix rows: {{self a . self b} . {aLine a . aLine b}}) determinant ] -{ #category : #'distance functions' } +{ #category : 'distance functions' } GLine >> distanceTo: aGPoint [ ^ (self a * aGPoint x + (self b * aGPoint y) + self c) abs / (self a squared + self b squared) sqrt ] -{ #category : #comparing } +{ #category : 'comparing' } GLine >> hash [ ^ ([ self yFor: 1 ] on: GeometryError do: [ "This can happen if b = 0" self xFor: 1 ]) hash ] -{ #category : #testing } +{ #category : 'testing' } GLine >> includes: aPoint [ ^ self a * aPoint x + (self b * aPoint y) + self c =~ 0 ] -{ #category : #intersections } +{ #category : 'intersections' } GLine >> intersectionsWith: anElement [ ^ anElement intersectionsWithLine: self ] -{ #category : #intersections } +{ #category : 'intersections' } GLine >> intersectionsWithEllipse: aGEllipse [ ^ aGEllipse intersectionsWithLine: self ] -{ #category : #intersections } +{ #category : 'intersections' } GLine >> intersectionsWithLine: aGLine [ | p q r x y determinant | determinant := self determinantWith: aGLine. @@ -152,26 +154,26 @@ GLine >> intersectionsWithLine: aGLine [ ^ { (x , y) } ] -{ #category : #properties } +{ #category : 'properties' } GLine >> isParallelTo: aLine [ "If the determinant it 0 then the lines are parallel" ^ (self determinantWith: aLine) =~ 0 ] -{ #category : #accessing } +{ #category : 'accessing' } GLine >> length [ ^ Float infinity ] -{ #category : #accessing } +{ #category : 'accessing' } GLine >> linearEquation [ "Return a dictionary with value for a, b and c representing the line with an equation of the form: ax + by + c = 0" ^ equationCache ifNil: [ equationCache := self privateLinearEquationComputation ] ] -{ #category : #printing } +{ #category : 'printing' } GLine >> printOn: aStream [ self a ~~ 0 ifTrue: [ self a ~~ 1 ifTrue: [ self a printOn: aStream ]. @@ -187,7 +189,7 @@ GLine >> printOn: aStream [ aStream nextPutAll: ' = 0' ] -{ #category : #private } +{ #category : 'private' } GLine >> privateLinearEquationComputation [ v1 y = v2 y ifTrue: [ ^ Dictionary with: #a -> 0 with: #b -> 1 with: #c -> v1 y negated ]. v1 x = v2 x ifTrue: [ ^ Dictionary with: #a -> 1 with: #b -> 0 with: #c -> v1 x negated ]. @@ -195,47 +197,47 @@ GLine >> privateLinearEquationComputation [ ^ Dictionary with: #a -> (v1 y - v2 y) with: #b -> (v2 x - v1 x) with: #c -> ((v1 x - v2 x) * v1 y + ((v2 y - v1 y) * v1 x)) ] -{ #category : #initialization } +{ #category : 'initialization' } GLine >> resetEquationCache [ equationCache := nil ] -{ #category : #transforming } +{ #category : 'transforming' } GLine >> translateBy: aGVector [ self v1: self v1 + aGVector. self v2: self v2 + aGVector ] -{ #category : #accessing } +{ #category : 'accessing' } GLine >> v1 [ ^ v1 ] -{ #category : #accessing } +{ #category : 'accessing' } GLine >> v1: aPoint [ v1 := aPoint. self resetEquationCache ] -{ #category : #accessing } +{ #category : 'accessing' } GLine >> v2 [ ^ v2 ] -{ #category : #accessing } +{ #category : 'accessing' } GLine >> v2: aPoint [ v2 := aPoint. self resetEquationCache ] -{ #category : #properties } +{ #category : 'properties' } GLine >> xFor: anY [ self a = 0 ifTrue: [ self error: 'Cannot answer a x if a = 0' ]. ^ ((anY * self b + self c) / self a) negated ] -{ #category : #properties } +{ #category : 'properties' } GLine >> yFor: anX [ self b = 0 ifTrue: [ self error: 'Cannot answer an y if b = 0' ]. diff --git a/src/Geometry/GMatrix.class.st b/src/Geometry/GMatrix.class.st index e6ee445..c8c2844 100644 --- a/src/Geometry/GMatrix.class.st +++ b/src/Geometry/GMatrix.class.st @@ -15,22 +15,24 @@ Internal Representation and Key Implementation Points. " Class { - #name : #GMatrix, - #superclass : #GeometryObject, + #name : 'GMatrix', + #superclass : 'GeometryObject', #instVars : [ 'rows' ], - #category : #'Geometry-Core' + #category : 'Geometry-Core', + #package : 'Geometry', + #tag : 'Core' } -{ #category : #'instance creation' } +{ #category : 'instance creation' } GMatrix class >> rows: aCollection [ ^ self new rows: aCollection; yourself ] -{ #category : #comparing } +{ #category : 'comparing' } GMatrix >> = anObject [ "Answer whether the receiver and anObject represent the same object." @@ -39,35 +41,35 @@ GMatrix >> = anObject [ ^ rows = anObject rows ] -{ #category : #accessing } +{ #category : 'accessing' } GMatrix >> at: anInteger [ ^ self rows at: anInteger ] -{ #category : #accessing } +{ #category : 'accessing' } GMatrix >> at: anInteger at: anInteger2 [ ^ (self at: anInteger) at: anInteger2 ] -{ #category : #arithmetic } +{ #category : 'arithmetic' } GMatrix >> determinant [ self flag: #todo. "For now this implementation is only for 2D squared matrix. We should later manage n-dimensions matrix" ^ (self at: 1 at: 1) * (self at: 2 at: 2) - ((self at: 1 at: 2) * (self at: 2 at: 1)) ] -{ #category : #comparing } +{ #category : 'comparing' } GMatrix >> hash [ "Answer an integer value that is related to the identity of the receiver." ^ rows hash negated ] -{ #category : #accessing } +{ #category : 'accessing' } GMatrix >> rows [ ^ rows ] -{ #category : #accessing } +{ #category : 'accessing' } GMatrix >> rows: anObject [ rows := anObject ] diff --git a/src/Geometry/GPoint.class.st b/src/Geometry/GPoint.class.st index 23c072a..ec13eda 100644 --- a/src/Geometry/GPoint.class.st +++ b/src/Geometry/GPoint.class.st @@ -45,36 +45,38 @@ Internal Representation and Key Implementation Points. " Class { - #name : #GPoint, - #superclass : #G1DElement, + #name : 'GPoint', + #superclass : 'G1DElement', #traits : 'TGWithCoordinates', #classTraits : 'TGWithCoordinates classTrait', #instVars : [ 'coordinates' ], - #category : #'Geometry-Core' + #category : 'Geometry-Core', + #package : 'Geometry', + #tag : 'Core' } -{ #category : #'instance creation' } +{ #category : 'instance creation' } GPoint class >> coordinates: aCoordinates [ ^ self new coordinates: aCoordinates; yourself ] -{ #category : #'instance creation' } +{ #category : 'instance creation' } GPoint class >> withCollection: aCollection [ ^ self coordinates: (GCoordinates withCollection: aCollection) ] -{ #category : #'instance creation' } +{ #category : 'instance creation' } GPoint class >> x: aNumber y: anotherNumber [ ^ self new coordinates: (G2DCoordinates x: aNumber y: anotherNumber); yourself ] -{ #category : #arithmetic } +{ #category : 'arithmetic' } GPoint >> + aGVector [ "I return a new point translated by a vector" @@ -83,14 +85,14 @@ GPoint >> + aGVector [ yourself ] -{ #category : #arithmetic } +{ #category : 'arithmetic' } GPoint >> - aPoint [ "I return a vector represented by the substraction of two points" ^ (self coordinates - aPoint coordinates) asGVector ] -{ #category : #comparing } +{ #category : 'comparing' } GPoint >> = anotherPoint [ "Answer whether the receiver and anObject represent the same object." @@ -100,38 +102,38 @@ GPoint >> = anotherPoint [ ^ coordinates = anotherPoint coordinates ] -{ #category : #comparing } +{ #category : 'comparing' } GPoint >> =~ aPoint [ ^ (self distanceTo: aPoint) =~ 0 ] -{ #category : #arithmetic } +{ #category : 'arithmetic' } GPoint >> additionWithVector: aGVector [ ^ self + aGVector ] -{ #category : #converting } +{ #category : 'converting' } GPoint >> asGPoint [ ^ self ] -{ #category : #converting } +{ #category : 'converting' } GPoint >> asPoint [ ^ self x @ self y ] -{ #category : #accessing } +{ #category : 'accessing' } GPoint >> coordinates [ ^ coordinates ] -{ #category : #accessing } +{ #category : 'accessing' } GPoint >> coordinates: anObject [ coordinates := anObject ] -{ #category : #comparing } +{ #category : 'comparing' } GPoint >> distanceTo: aGPoint [ "Answer the distance between aPoint and the receiver. @@ -140,41 +142,41 @@ GPoint >> distanceTo: aGPoint [ ^ (self - aGPoint) length ] -{ #category : #comparing } +{ #category : 'comparing' } GPoint >> hash [ "Answer an integer value that is related to the identity of the receiver." ^ coordinates hash squared "Try to avoid collisions with GAbstractCoordinates" ] -{ #category : #testing } +{ #category : 'testing' } GPoint >> includes: aGPoint [ ^ aGPoint =~ self ] -{ #category : #intersections } +{ #category : 'intersections' } GPoint >> intersectionsWith: anElement [ ^ anElement intersectionsWithPoint: self ] -{ #category : #intersections } +{ #category : 'intersections' } GPoint >> intersectionsWithEllipse: aGEllipse [ ^ aGEllipse intersectionsWithPoint: self ] -{ #category : #intersections } +{ #category : 'intersections' } GPoint >> intersectionsWithLine: aGLine [ ^ aGLine intersectionsWithPoint: self ] -{ #category : #accessing } +{ #category : 'accessing' } GPoint >> length [ "https://math.stackexchange.com/questions/1936865/what-is-the-length-of-a-point-on-the-real-number-line" ^ 0 ] -{ #category : #transformations } +{ #category : 'transformations' } GPoint >> rotateBy: aGAngle [ "Rotate the point around the origin (0,0) of the plan. @@ -185,7 +187,7 @@ GPoint >> rotateBy: aGAngle [ self rotateBy: aGAngle about: 0 , 0 ] -{ #category : #transformations } +{ #category : 'transformations' } GPoint >> rotateBy: aGAngle about: aGPoint [ "Rotate the point around the another point of the plan. @@ -202,7 +204,7 @@ GPoint >> rotateBy: aGAngle about: aGPoint [ self y: sin * diffX + (cos * diffY) + aGPoint y ] -{ #category : #transformations } +{ #category : 'transformations' } GPoint >> rotatedBy: aGAngle [ "Rotate the point around the origin (0,0) of the plan. @@ -213,7 +215,7 @@ GPoint >> rotatedBy: aGAngle [ ^ self rotatedBy: aGAngle about: 0 , 0 ] -{ #category : #transformations } +{ #category : 'transformations' } GPoint >> rotatedBy: aGAngle about: aGPoint [ "Rotate the point around another point of the plan. @@ -229,12 +231,12 @@ GPoint >> rotatedBy: aGAngle about: aGPoint [ ^ cos * diffX - (sin * diffY) + aGPoint x , (sin * diffX + (cos * diffY) + aGPoint y) ] -{ #category : #accessing } +{ #category : 'accessing' } GPoint >> segment: aPoint [ ^ GSegment with: self with: aPoint ] -{ #category : #transforming } +{ #category : 'transforming' } GPoint >> translateBy: aVector [ "Answer a Point translated by a vector." diff --git a/src/Geometry/GPolygon.class.st b/src/Geometry/GPolygon.class.st index 22a4bf1..b65f0c7 100644 --- a/src/Geometry/GPolygon.class.st +++ b/src/Geometry/GPolygon.class.st @@ -4,15 +4,17 @@ I'm polygon builded on my vertices. " Class { - #name : #GPolygon, - #superclass : #GShape, + #name : 'GPolygon', + #superclass : 'GShape', #instVars : [ 'vertices' ], - #category : #'Geometry-Shapes' + #category : 'Geometry-Shapes', + #package : 'Geometry', + #tag : 'Shapes' } -{ #category : #public } +{ #category : 'public' } GPolygon class >> convexHullOn: aCollection [ | lastPoint nextPoint convexHull | "self halt." @@ -35,7 +37,7 @@ GPolygon class >> convexHullOn: aCollection [ ^ self vertices: convexHull ] -{ #category : #'instance creation' } +{ #category : 'instance creation' } GPolygon class >> newRegularPolygonWithEdgeNumber: aNumber [ "I return the polygon vertices for a regular polygon with a number of segment given as parameter. @@ -47,20 +49,20 @@ GPolygon class >> newRegularPolygonWithEdgeNumber: aNumber [ ^ self newVertices: ((1 to: aNumber) collect: [ :index | ((encompassingCircleRadius + index) * angle) sin , ((encompassingCircleRadius + index) * angle) cos negated ]) ] -{ #category : #'instance creation' } +{ #category : 'instance creation' } GPolygon class >> newVertices: aCollection [ self deprecated: 'Use #vertices: instead' transformWith: '`@receiver newVertices: `@statements' -> '`@receiver vertices: `@statements'. ^ self vertices: aCollection ] -{ #category : #'instance creation' } +{ #category : 'instance creation' } GPolygon class >> vertices: aCollection [ ^ self new vertices: aCollection; yourself ] -{ #category : #comparing } +{ #category : 'comparing' } GPolygon >> = anObject [ "Answer whether the receiver and anObject represent the same object." @@ -70,12 +72,12 @@ GPolygon >> = anObject [ ^ vertices = anObject vertices ] -{ #category : #testing } +{ #category : 'testing' } GPolygon >> boundaryContains: aGPoint [ ^ self edges anySatisfy: [ :segment | segment includes: aGPoint ] ] -{ #category : #accessing } +{ #category : 'accessing' } GPolygon >> edges [ | edges | edges := (self vertices overlappingPairsCollect: [ :point1 :point2 | (GSegment with: point1 with: point2) ]) asOrderedCollection. @@ -83,7 +85,7 @@ GPolygon >> edges [ ^ edges ] -{ #category : #accessing } +{ #category : 'accessing' } GPolygon >> encompassingRectangle [ | origin corner | origin := ((self vertices collect: #coordinates) fold: [ :coor1 :coor2 | coor1 lowerLimitsWith: coor2 ]) asGPoint. @@ -91,12 +93,12 @@ GPolygon >> encompassingRectangle [ ^ GRectangle origin: origin corner: corner ] -{ #category : #'rectangle functions' } +{ #category : 'rectangle functions' } GPolygon >> expandBy: anInteger [ self fitInExtent: self extent + (2 * {anInteger . anInteger} asGVector) ] -{ #category : #transforming } +{ #category : 'transforming' } GPolygon >> fitInExtent: extent [ "I take as parameter a point and will fit the polygon in a rectangle whose dimensions is defined by the extent." @@ -105,14 +107,14 @@ GPolygon >> fitInExtent: extent [ vertices := self vertices collect: [ :vertice | ((vertice - self center) coordinates coordinatesWith: scales collect: [ :a :b | a * b ]) asGVector + self center ] ] -{ #category : #comparing } +{ #category : 'comparing' } GPolygon >> hash [ "Answer an integer value that is related to the identity of the receiver." ^ vertices hash bitXor: self class hash ] -{ #category : #testing } +{ #category : 'testing' } GPolygon >> includes: aPoint [ " Thanks to Google and Randolph Franklin i don't have to reinvent this very simple algorithm. See [ 1 ] for details, copyrights etc. @@ -135,27 +137,27 @@ GPolygon >> includes: aPoint [ ^ inside ] -{ #category : #intersections } +{ #category : 'intersections' } GPolygon >> intersectionsWith: anElement [ ^ anElement intersectionsWithPolygon: self ] -{ #category : #intersections } +{ #category : 'intersections' } GPolygon >> intersectionsWithEllipse: aGEllipse [ ^ aGEllipse intersectionsWithPolygon: self ] -{ #category : #intersections } +{ #category : 'intersections' } GPolygon >> intersectionsWithLine: aGLine [ ^ aGLine intersectionsWithPolygon: self ] -{ #category : #accessing } +{ #category : 'accessing' } GPolygon >> perimeter [ ^ self edges sum: #length ] -{ #category : #transforming } +{ #category : 'transforming' } GPolygon >> scaleBy: anInteger [ anInteger <= 0 ifTrue: [ self error: 'Scale must be over zeo' ]. @@ -163,20 +165,20 @@ GPolygon >> scaleBy: anInteger [ vertices := self vertices collect: [ :vertice | (vertice - self center) * anInteger + self center ] ] -{ #category : #transforming } +{ #category : 'transforming' } GPolygon >> translateBy: aPoint [ "Move a polygon by a delta defined by aPoint" vertices := self vertices collect: [ :point | point + aPoint ] ] -{ #category : #accessing } +{ #category : 'accessing' } GPolygon >> vertices [ ^ vertices ] -{ #category : #accessing } +{ #category : 'accessing' } GPolygon >> vertices: aCollection [ vertices := aCollection collect: #asGPoint ] diff --git a/src/Geometry/GRay.class.st b/src/Geometry/GRay.class.st index 801d705..2c15264 100644 --- a/src/Geometry/GRay.class.st +++ b/src/Geometry/GRay.class.st @@ -28,16 +28,18 @@ Internal Representation and Key Implementation Points. " Class { - #name : #GRay, - #superclass : #G1DElement, + #name : 'GRay', + #superclass : 'G1DElement', #instVars : [ 'initialPoint', 'directionPoint' ], - #category : #'Geometry-Elements' + #category : 'Geometry-Elements', + #package : 'Geometry', + #tag : 'Elements' } -{ #category : #'instance creation' } +{ #category : 'instance creation' } GRay class >> origin: aGPoint direction: anotherPoint [ ^ self new initialPoint: aGPoint; @@ -45,7 +47,7 @@ GRay class >> origin: aGPoint direction: anotherPoint [ yourself ] -{ #category : #comparing } +{ #category : 'comparing' } GRay >> = aRay [ self == aRay ifTrue: [ ^ true ]. self class = aRay class ifFalse: [ ^ false ]. @@ -53,78 +55,78 @@ GRay >> = aRay [ ^ self initialPoint =~ aRay initialPoint and: [ (self angleWith: aRay) isZero ] ] -{ #category : #arithmetic } +{ #category : 'arithmetic' } GRay >> angleWith: aGRay [ ^ (self directionPoint - self initialPoint) angleWith: (aGRay directionPoint - aGRay initialPoint) ] -{ #category : #converting } +{ #category : 'converting' } GRay >> asGLine [ ^ GLine through: self initialPoint and: self directionPoint ] -{ #category : #accessing } +{ #category : 'accessing' } GRay >> directionPoint [ ^ directionPoint ] -{ #category : #accessing } +{ #category : 'accessing' } GRay >> directionPoint: aGPoint [ directionPoint := aGPoint asGPoint ] -{ #category : #properties } +{ #category : 'properties' } GRay >> flipped [ ^ self class origin: self initialPoint direction: initialPoint + (initialPoint - directionPoint) ] -{ #category : #comparing } +{ #category : 'comparing' } GRay >> hash [ "We define the hash by its origin and the angle the ray has with the x axis." ^ self initialPoint hash bitXor: (self directionPoint - self initialPoint angleWith: {1 . 1} asGVector) hash ] -{ #category : #testing } +{ #category : 'testing' } GRay >> includes: aPoint [ ^ ((self directionPoint - self initialPoint) angleWith: (aPoint - self initialPoint)) isZero ] -{ #category : #accessing } +{ #category : 'accessing' } GRay >> initialPoint [ ^ initialPoint ] -{ #category : #accessing } +{ #category : 'accessing' } GRay >> initialPoint: aGPoint [ initialPoint := aGPoint asGPoint ] -{ #category : #intersections } +{ #category : 'intersections' } GRay >> intersectionsWith: anElement [ ^ anElement intersectionsWithRay: self ] -{ #category : #intersections } +{ #category : 'intersections' } GRay >> intersectionsWithEllipse: aGEllipse [ ^ aGEllipse intersectionsWithRay: self ] -{ #category : #intersections } +{ #category : 'intersections' } GRay >> intersectionsWithLine: aGLine [ ^ aGLine intersectionsWithRay: self ] -{ #category : #accessing } +{ #category : 'accessing' } GRay >> length [ "A ray is not finite." ^ Float infinity ] -{ #category : #transforming } +{ #category : 'transforming' } GRay >> translateBy: aGVector [ initialPoint := initialPoint + aGVector. directionPoint := directionPoint + aGVector diff --git a/src/Geometry/GRectangle.class.st b/src/Geometry/GRectangle.class.st index a02812a..9180be1 100644 --- a/src/Geometry/GRectangle.class.st +++ b/src/Geometry/GRectangle.class.st @@ -2,12 +2,14 @@ I am a rectangle. See my parent for API " Class { - #name : #GRectangle, - #superclass : #GPolygon, - #category : #'Geometry-Shapes' + #name : 'GRectangle', + #superclass : 'GPolygon', + #category : 'Geometry-Shapes', + #package : 'Geometry', + #tag : 'Shapes' } -{ #category : #'instance creation' } +{ #category : 'instance creation' } GRectangle class >> left: left right: right top: top bottom: bottom [ "Answer an instance of me whose left, right, top, and bottom coordinates are determined by the arguments." @@ -19,7 +21,7 @@ GRectangle class >> left: left right: right top: top bottom: bottom [ ^ self origin: origin corner: corner ] -{ #category : #'instance creation' } +{ #category : 'instance creation' } GRectangle class >> origin: point1 corner: point2 [ "Answer an instance of me whose corners (top left and bottom right) are determined by the arguments." @@ -33,7 +35,7 @@ GRectangle class >> origin: point1 corner: point2 [ ^ self vertices: { or . (cor x , or y). cor. (or x , cor y) } ] -{ #category : #'instance creation' } +{ #category : 'instance creation' } GRectangle class >> vertices: aCollection [ | rectangle edges | @@ -48,27 +50,27 @@ GRectangle class >> vertices: aCollection [ ^ rectangle ] -{ #category : #accessing } +{ #category : 'accessing' } GRectangle >> area [ "Answer the receiver's area, the product of width and height." ^ self width * self height max: 0 ] -{ #category : #accessing } +{ #category : 'accessing' } GRectangle >> center [ "Answer the point at the center of the receiver." ^ self diagonals anyOne midPoint ] -{ #category : #accessing } +{ #category : 'accessing' } GRectangle >> diagonals [ ^ {((self vertices at: 1) segment: (self vertices at: 3)). ((self vertices at: 2) segment: (self vertices at: 4))} ] -{ #category : #accessing } +{ #category : 'accessing' } GRectangle >> extent [ "Answer a vector representing the extent of my encompassing rectangle." @@ -79,14 +81,14 @@ GRectangle >> extent [ ^ (encompassingRectangleOrigin - encompassingRectangleCorner) asGVector ] -{ #category : #accessing } +{ #category : 'accessing' } GRectangle >> height [ "Answer the height of the receiver." ^ (self vertices at: 2) distanceTo: (self vertices at: 3) ] -{ #category : #accessing } +{ #category : 'accessing' } GRectangle >> width [ "Answer the width of the receiver." diff --git a/src/Geometry/GSegment.class.st b/src/Geometry/GSegment.class.st index 83707d8..96516f4 100644 --- a/src/Geometry/GSegment.class.st +++ b/src/Geometry/GSegment.class.st @@ -30,16 +30,18 @@ Internal Representation and Key Implementation Points. " Class { - #name : #GSegment, - #superclass : #G1DElement, + #name : 'GSegment', + #superclass : 'G1DElement', #instVars : [ 'v1', 'v2' ], - #category : #'Geometry-Elements' + #category : 'Geometry-Elements', + #package : 'Geometry', + #tag : 'Elements' } -{ #category : #'instance creation' } +{ #category : 'instance creation' } GSegment class >> with: aPoint1 with: aPoint2 [ ^ self new v1: aPoint1; @@ -47,7 +49,7 @@ GSegment class >> with: aPoint1 with: aPoint2 [ yourself ] -{ #category : #comparing } +{ #category : 'comparing' } GSegment >> = aSegment [ self == aSegment ifTrue: [ ^ true ]. self class = aSegment class ifFalse: [ ^ false ]. @@ -56,104 +58,115 @@ GSegment >> = aSegment [ or: [ v1 =~ aSegment v2 and: [ v2 =~ aSegment v1 ] ] ] -{ #category : #arithmetic } +{ #category : 'arithmetic' } GSegment >> angleWith: aSegment [ ^ self v1 - self v2 angleWith: aSegment v1 - aSegment v2 ] -{ #category : #properties } +{ #category : 'properties' } GSegment >> asGLine [ ^ GLine through: v1 and: v2 ] -{ #category : #properties } +{ #category : 'properties' } GSegment >> distanceTo: aGPoint [ - self flag: #todo. "«The distance between segments and rays may not be the same as the distance between their extended lines. » http://geomalgorithms.com/a07-_distance.html + + | ab ap t proj distance | + + ab := (v2 asPoint) - (v1 asPoint). + ap := (aGPoint asPoint) - (v1 asPoint). + t := (ab dotProduct: ap) / (ab dotProduct: ab). + t := (t max: 0) min: 1. + + proj := (v1 asPoint) + (ab * t). + distance := (aGPoint asPoint) distanceTo: proj. + + ^distance + + - So apparently this implementation is wrong." - ^ self asGLine distanceTo: aGPoint ] -{ #category : #comparing } +{ #category : 'comparing' } GSegment >> hash [ ^ v1 hash bitXor: v2 hash ] -{ #category : #properties } +{ #category : 'properties' } GSegment >> includes: aGPoint [ "The point is on the segment if the sum of the distance from v1 to aGPoint and the distance of a GPoint to v2 is my length" ^ (v2 distanceTo: aGPoint) + (aGPoint distanceTo: v1) =~ self length ] -{ #category : #intersections } +{ #category : 'intersections' } GSegment >> intersectionsWith: anElement [ ^ anElement intersectionsWithSegment: self ] -{ #category : #intersections } +{ #category : 'intersections' } GSegment >> intersectionsWithEllipse: aGEllipse [ ^ aGEllipse intersectionsWithSegment: self ] -{ #category : #intersections } +{ #category : 'intersections' } GSegment >> intersectionsWithLine: aLine [ ^ aLine intersectionsWithSegment: self ] -{ #category : #properties } +{ #category : 'properties' } GSegment >> length [ "The distance between two points is the length of the vector transposing v1 into v2" ^ (v2 - v1) length ] -{ #category : #properties } +{ #category : 'properties' } GSegment >> midPoint [ ^ ((v1 coordinates + v2 coordinates) / 2) asGPoint ] -{ #category : #properties } +{ #category : 'properties' } GSegment >> perpendicularBisector [ self flag: #todo. "This is only for 2D. We should rely on GPoints to be able to do it for n-dimensions." ^ GLine a: v2 x - v1 x b: v2 y - v1 y c: (v1 x * v1 x - (v2 x * v2 x) + (v1 y * v1 y) - (v2 y * v2 y)) / 2 ] -{ #category : #printing } +{ #category : 'printing' } GSegment >> printOn: aStream [ v1 coordinates printOn: aStream. aStream nextPutAll: '>-<'. v2 coordinates printOn: aStream ] -{ #category : #transforming } +{ #category : 'transforming' } GSegment >> translateBy: aGVector [ v1 := v1 + aGVector. v2 := v2 + aGVector ] -{ #category : #accessing } +{ #category : 'accessing' } GSegment >> v1 [ ^ v1 ] -{ #category : #accessing } +{ #category : 'accessing' } GSegment >> v1: aGPoint [ v1 := aGPoint asGPoint ] -{ #category : #accessing } +{ #category : 'accessing' } GSegment >> v2 [ ^ v2 ] -{ #category : #accessing } +{ #category : 'accessing' } GSegment >> v2: aGPoint [ v2 := aGPoint asGPoint ] -{ #category : #initialization } +{ #category : 'initialization' } GSegment >> vertices [ ^ { v1 . v2 } ] diff --git a/src/Geometry/GShape.class.st b/src/Geometry/GShape.class.st index 3cae50b..dc8cb03 100644 --- a/src/Geometry/GShape.class.st +++ b/src/Geometry/GShape.class.st @@ -7,43 +7,45 @@ I am a common superclass for all shapes elements. I define the minimal API each elements should be able to answer to. " Class { - #name : #GShape, - #superclass : #GElement, - #category : #'Geometry-Shapes' + #name : 'GShape', + #superclass : 'GElement', + #category : 'Geometry-Shapes', + #package : 'Geometry', + #tag : 'Shapes' } -{ #category : #testing } +{ #category : 'testing' } GShape class >> isAbstract [ ^ self = GShape ] -{ #category : #accessing } +{ #category : 'accessing' } GShape >> area [ "Return the area of the shape" ^ self subclassResponsibility ] -{ #category : #accessing } +{ #category : 'accessing' } GShape >> center [ ^ self encompassingRectangle center ] -{ #category : #accessing } +{ #category : 'accessing' } GShape >> encompassingRectangle [ "I should return a rectangle on the minimum size that contains the shape" ^ self subclassResponsibility ] -{ #category : #accessing } +{ #category : 'accessing' } GShape >> extent [ "I return a vector representing the extent of the encompassing rectangle of the shape. I should be able to translate the corner of the encompassing rectangle to its origin." ^ self encompassingRectangle extent ] -{ #category : #transforming } +{ #category : 'transforming' } GShape >> fitInExtent: anExtent [ "I should transform my shape to fit in a rectangle of size 0@0 to the extent as parameter, keeping my center. Note that I should keep proportions. We could maybe implement another method to not keep proportions." @@ -51,21 +53,21 @@ GShape >> fitInExtent: anExtent [ self subclassResponsibility ] -{ #category : #accessing } +{ #category : 'accessing' } GShape >> perimeter [ "Return the perimeter of the shape" ^ self subclassResponsibility ] -{ #category : #transforming } +{ #category : 'transforming' } GShape >> scaleBy: aPoint [ "Scale the shape by a delta defined by a point." self subclassResponsibility ] -{ #category : #properties } +{ #category : 'properties' } GShape >> semiperimeter [ ^ self perimeter / 2 ] diff --git a/src/Geometry/GTriangle.class.st b/src/Geometry/GTriangle.class.st index d7c2d0b..a0f6b32 100644 --- a/src/Geometry/GTriangle.class.st +++ b/src/Geometry/GTriangle.class.st @@ -2,24 +2,26 @@ I am a triangle " Class { - #name : #GTriangle, - #superclass : #GPolygon, - #category : #'Geometry-Shapes' + #name : 'GTriangle', + #superclass : 'GPolygon', + #category : 'Geometry-Shapes', + #package : 'Geometry', + #tag : 'Shapes' } -{ #category : #'instance creation' } +{ #category : 'instance creation' } GTriangle class >> vertices: aCollection [ aCollection size = 3 ifFalse: [ self error: 'A triangle should have 3 vertices.' ]. ^ super vertices: aCollection ] -{ #category : #'instance creation' } +{ #category : 'instance creation' } GTriangle class >> with: aPoint1 with: aPoint2 with: aPoint3 [ ^ self vertices: { aPoint1. aPoint2. aPoint3 } ] -{ #category : #properties } +{ #category : 'properties' } GTriangle >> area [ | semiperimeter| semiperimeter := self semiperimeter. @@ -30,13 +32,13 @@ GTriangle >> area [ (semiperimeter - edge length) * subProduct ]) sqrt ] -{ #category : #figures } +{ #category : 'figures' } GTriangle >> circumscribedCircle [ ^ (self edges first perpendicularBisector intersectionsWith: self edges last perpendicularBisector) ifNotEmpty: [ :points | GCircle center: points anyOne radius: (self v1 distanceTo: self v2) * (self v2 distanceTo: self v3) * (self v3 distanceTo: self v1) / (4 * self area) ] ] -{ #category : #figures } +{ #category : 'figures' } GTriangle >> isDegenerate [ ^ (self v1 = self v2 or: [ self v1 = self v3 or: [ self v2 = self v3 ] ]) ifTrue: [ true ] @@ -44,17 +46,17 @@ GTriangle >> isDegenerate [ or: [ self edges second length + self edges third length = self edges first length or: [ self edges third length + self edges first length = self edges second length ] ] ] ] -{ #category : #accessing } +{ #category : 'accessing' } GTriangle >> v1 [ ^ vertices at: 1 ] -{ #category : #accessing } +{ #category : 'accessing' } GTriangle >> v2 [ ^ vertices at: 2 ] -{ #category : #accessing } +{ #category : 'accessing' } GTriangle >> v3 [ ^ vertices at: 3 ] diff --git a/src/Geometry/GVector.class.st b/src/Geometry/GVector.class.st index 8759bc4..0534034 100644 --- a/src/Geometry/GVector.class.st +++ b/src/Geometry/GVector.class.st @@ -29,17 +29,19 @@ Internal Representation and Key Implementation Points. " Class { - #name : #GVector, - #superclass : #GeometryObject, + #name : 'GVector', + #superclass : 'GeometryObject', #traits : 'TGWithCoordinates', #classTraits : 'TGWithCoordinates classTrait', #instVars : [ 'coordinates' ], - #category : #'Geometry-Core' + #category : 'Geometry-Core', + #package : 'Geometry', + #tag : 'Core' } -{ #category : #'instance creation' } +{ #category : 'instance creation' } GVector class >> coordinates: aCoordinates [ (aCoordinates coordinates allSatisfy: [ :e | e = 0]) ifTrue: [ ^ GZeroVector coordinates: aCoordinates ]. @@ -48,32 +50,32 @@ GVector class >> coordinates: aCoordinates [ yourself ] -{ #category : #'instance creation' } +{ #category : 'instance creation' } GVector class >> withCollection: aCollection [ ^ self coordinates: (GCoordinates withCollection: aCollection) ] -{ #category : #'instance creation' } +{ #category : 'instance creation' } GVector class >> x: aNumber y: anotherNumber [ ^ self coordinates: (G2DCoordinates x: aNumber y: anotherNumber) ] -{ #category : #arithmetic } +{ #category : 'arithmetic' } GVector >> * anOperand [ ^ anOperand multiplyWithVector: self ] -{ #category : #arithmetic } +{ #category : 'arithmetic' } GVector >> + anOperand [ ^ anOperand additionWithVector: self ] -{ #category : #arithmetic } +{ #category : 'arithmetic' } GVector >> / aGVector [ self shouldBeImplemented. ] -{ #category : #comparing } +{ #category : 'comparing' } GVector >> = anotherPoint [ "Answer whether the receiver and anObject represent the same object." @@ -83,17 +85,17 @@ GVector >> = anotherPoint [ ^ coordinates = anotherPoint coordinates ] -{ #category : #arithmetic } +{ #category : 'arithmetic' } GVector >> adaptToNumber: anInteger andSend: aString [ ^ self perform: aString with: anInteger ] -{ #category : #arithmetic } +{ #category : 'arithmetic' } GVector >> additionWithVector: aGVector [ ^ (self coordinates + aGVector coordinates) asGVector ] -{ #category : #arithmetic } +{ #category : 'arithmetic' } GVector >> angleWith: aGVector [ | angle | self flag: #todo. "This implementation is only for 2D vectors. Later we should support it for n-dimension vectors." @@ -104,22 +106,22 @@ GVector >> angleWith: aGVector [ ^ (GMatrix rows: {self coordinates . aGVector coordinates}) determinant > 0 ifTrue: [ angle ] ifFalse: [ angle explementary ] ] -{ #category : #accessing } +{ #category : 'accessing' } GVector >> coordinates [ ^ coordinates ] -{ #category : #accessing } +{ #category : 'accessing' } GVector >> coordinates: anObject [ coordinates := anObject ] -{ #category : #arithmetic } +{ #category : 'arithmetic' } GVector >> dotProduct: aGVector [ ^ (self coordinatesWith: aGVector coordinates collect: [ :point1 :point2 | point1 * point2 ]) sum ] -{ #category : #comparing } +{ #category : 'comparing' } GVector >> hash [ "Answer an integer value that is related to the identity of the receiver." @@ -128,26 +130,26 @@ GVector >> hash [ ^ coordHash squared + coordHash "Try to avoid collisions with GAbstractCoordinates and GPoint" ] -{ #category : #testing } +{ #category : 'testing' } GVector >> isZeroVector [ ^ false ] -{ #category : #accessing } +{ #category : 'accessing' } GVector >> length [ "https://onlinemschool.com/math/library/vector/length/" ^ (self coordinates inject: 0 into: [ :res :number | res + number squared ]) sqrt ] -{ #category : #arithmetic } +{ #category : 'arithmetic' } GVector >> multiplyWithVector: aGVector [ "We should implement vector product later." self shouldBeImplemented ] -{ #category : #arithmetic } +{ #category : 'arithmetic' } GVector >> nonOrientedAngleWith: aGVector [ "angle = cos^-1 a . b / (|a| * |b|)" diff --git a/src/Geometry/GZeroVector.class.st b/src/Geometry/GZeroVector.class.st index 3491b33..f74d25a 100644 --- a/src/Geometry/GZeroVector.class.st +++ b/src/Geometry/GZeroVector.class.st @@ -5,24 +5,26 @@ Description I represent the zero vector that is a vector of length 0 and the only vector without direction. " Class { - #name : #GZeroVector, - #superclass : #GVector, - #category : #'Geometry-Core' + #name : 'GZeroVector', + #superclass : 'GVector', + #category : 'Geometry-Core', + #package : 'Geometry', + #tag : 'Core' } -{ #category : #'instance creation' } +{ #category : 'instance creation' } GZeroVector class >> coordinates: aCoordinates [ ^ self new coordinates: aCoordinates; yourself ] -{ #category : #arithmetic } +{ #category : 'arithmetic' } GZeroVector >> angleWith: aGVector [ ^ 0 rads ] -{ #category : #testing } +{ #category : 'testing' } GZeroVector >> isZeroVector [ ^ true ] diff --git a/src/Geometry/GeometryError.class.st b/src/Geometry/GeometryError.class.st index e4a17b2..8c3f375 100644 --- a/src/Geometry/GeometryError.class.st +++ b/src/Geometry/GeometryError.class.st @@ -5,7 +5,9 @@ Description I am a common exception for the Geometry project " Class { - #name : #GeometryError, - #superclass : #Error, - #category : #'Geometry-Exceptions' + #name : 'GeometryError', + #superclass : 'Error', + #category : 'Geometry-Exceptions', + #package : 'Geometry', + #tag : 'Exceptions' } diff --git a/src/Geometry/GeometryObject.class.st b/src/Geometry/GeometryObject.class.st index 15c338b..61e65f5 100644 --- a/src/Geometry/GeometryObject.class.st +++ b/src/Geometry/GeometryObject.class.st @@ -5,27 +5,29 @@ Description I am a common superclass for all Geometry object. Concrete (points, segments, polygons...) and abstract objects (coordinates, vectors, ...). " Class { - #name : #GeometryObject, - #superclass : #Object, - #category : #'Geometry-Elements' + #name : 'GeometryObject', + #superclass : 'Object', + #category : 'Geometry-Elements', + #package : 'Geometry', + #tag : 'Elements' } -{ #category : #'error handling' } +{ #category : 'error handling' } GeometryObject class >> error [ GeometryError signal ] -{ #category : #'error handling' } +{ #category : 'error handling' } GeometryObject class >> error: aString [ GeometryError signal: aString ] -{ #category : #'error handling' } +{ #category : 'error handling' } GeometryObject >> error [ ^ self class error ] -{ #category : #'error handling' } +{ #category : 'error handling' } GeometryObject >> error: aString [ ^ self class error: aString ] diff --git a/src/Geometry/Number.extension.st b/src/Geometry/Number.extension.st index 44f7410..15e12ba 100644 --- a/src/Geometry/Number.extension.st +++ b/src/Geometry/Number.extension.st @@ -1,48 +1,48 @@ -Extension { #name : #Number } +Extension { #name : 'Number' } -{ #category : #'*Geometry' } +{ #category : '*Geometry' } Number >> , aNumber [ ^ GPoint x: self y: aNumber ] -{ #category : #'*Geometry' } +{ #category : '*Geometry' } Number >> =~ aNumber [ ^((self - aNumber) abs) < Number epsilon. ] -{ #category : #'*Geometry' } +{ #category : '*Geometry' } Number >> degrees [ ^ GAngle degrees: self ] -{ #category : #'*Geometry' } +{ #category : '*Geometry' } Number >> divideWithAngle: aGAngle [ ^ (aGAngle rads / self) rads ] -{ #category : #'*Geometry' } +{ #category : '*Geometry' } Number class >> epsilon [ ^ 0.00001 ] -{ #category : #'*Geometry' } +{ #category : '*Geometry' } Number >> multiplyWithAngle: aGAngle [ ^ (aGAngle rads * self) rads ] -{ #category : #'*Geometry' } +{ #category : '*Geometry' } Number >> multiplyWithVector: aGVector [ ^ (aGVector coordinates * self) asGVector ] -{ #category : #'*Geometry' } +{ #category : '*Geometry' } Number >> rads [ ^ GAngle radians: self ] -{ #category : #'*Geometry' } +{ #category : '*Geometry' } Number >> π [ ^ self * Float pi ] diff --git a/src/Geometry/Point.extension.st b/src/Geometry/Point.extension.st index a4ff9f2..bd004e4 100644 --- a/src/Geometry/Point.extension.st +++ b/src/Geometry/Point.extension.st @@ -1,11 +1,11 @@ -Extension { #name : #Point } +Extension { #name : 'Point' } -{ #category : #'*Geometry' } +{ #category : '*Geometry' } Point >> asGPoint [ ^ x , y ] -{ #category : #'*Geometry' } +{ #category : '*Geometry' } Point >> asGVector [ ^ GVector coordinates: (G2DCoordinates x: x y: y) ] diff --git a/src/Geometry/TGWithCoordinates.trait.st b/src/Geometry/TGWithCoordinates.trait.st index 901b601..40631c5 100644 --- a/src/Geometry/TGWithCoordinates.trait.st +++ b/src/Geometry/TGWithCoordinates.trait.st @@ -1,56 +1,58 @@ Trait { - #name : #TGWithCoordinates, - #category : #'Geometry-Core' + #name : 'TGWithCoordinates', + #category : 'Geometry-Core', + #package : 'Geometry', + #tag : 'Core' } -{ #category : #accessing } +{ #category : 'accessing' } TGWithCoordinates >> coordinates [ ^ self explicitRequirement ] -{ #category : #accessing } +{ #category : 'accessing' } TGWithCoordinates >> coordinates: gCoordinates [ ^ self explicitRequirement ] -{ #category : #enumerating } +{ #category : 'enumerating' } TGWithCoordinates >> coordinatesWith: aCollection collect: aBlock [ ^ self coordinates coordinatesWith: aCollection coordinates collect: aBlock ] -{ #category : #enumerating } +{ #category : 'enumerating' } TGWithCoordinates >> coordinatesWith: aCollection do: aBlock [ self coordinates coordinatesWith: aCollection coordinates do: aBlock ] -{ #category : #copying } +{ #category : 'copying' } TGWithCoordinates >> postCopy [ super postCopy. self coordinates: self coordinates copy ] -{ #category : #printing } +{ #category : 'printing' } TGWithCoordinates >> printOn: aStream [ super printOn: aStream. self coordinates printCoordinatesOn: aStream ] -{ #category : #accessing } +{ #category : 'accessing' } TGWithCoordinates >> x [ ^ self coordinates x ] -{ #category : #accessing } +{ #category : 'accessing' } TGWithCoordinates >> x: aNumber [ self coordinates x: aNumber ] -{ #category : #accessing } +{ #category : 'accessing' } TGWithCoordinates >> y [ ^ self coordinates y ] -{ #category : #accessing } +{ #category : 'accessing' } TGWithCoordinates >> y: aNumber [ self coordinates y: aNumber ] diff --git a/src/Geometry/package.st b/src/Geometry/package.st index c33ebb9..b8dbfec 100644 --- a/src/Geometry/package.st +++ b/src/Geometry/package.st @@ -1 +1 @@ -Package { #name : #Geometry } +Package { #name : 'Geometry' } From 89226d3d7af23b9fa1b15a7f117bb297a917b3a1 Mon Sep 17 00:00:00 2001 From: Brendan Date: Mon, 8 Dec 2025 15:05:16 +0100 Subject: [PATCH 2/6] Fix testing zero value --- src/Geometry/GSegment.class.st | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Geometry/GSegment.class.st b/src/Geometry/GSegment.class.st index 96516f4..7bffb3d 100644 --- a/src/Geometry/GSegment.class.st +++ b/src/Geometry/GSegment.class.st @@ -73,7 +73,9 @@ GSegment >> distanceTo: aGPoint [ | ab ap t proj distance | + distance := SmallInteger maxVal. ab := (v2 asPoint) - (v1 asPoint). + ab isZero ifTrue:[^distance]. ap := (aGPoint asPoint) - (v1 asPoint). t := (ab dotProduct: ap) / (ab dotProduct: ab). t := (t max: 0) min: 1. From 0016ff11af02148db599bea770f5c38586f0bd76 Mon Sep 17 00:00:00 2001 From: LANDAIS Brendan <116742582+LANDAISB@users.noreply.github.com> Date: Mon, 19 Jan 2026 17:14:09 +0100 Subject: [PATCH 3/6] Update .properties --- src/.properties | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/.properties b/src/.properties index ad0471d..db369e2 100644 --- a/src/.properties +++ b/src/.properties @@ -1,3 +1,4 @@ { #format : #tonel -} \ No newline at end of file + #version : #'1.0' +} From f6ad6d5b2dfe9e3521f14092d56f5fb3d613f176 Mon Sep 17 00:00:00 2001 From: LANDAIS Brendan <116742582+LANDAISB@users.noreply.github.com> Date: Mon, 19 Jan 2026 17:22:29 +0100 Subject: [PATCH 4/6] Update .properties --- src/.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/.properties b/src/.properties index db369e2..6e0f77c 100644 --- a/src/.properties +++ b/src/.properties @@ -1,4 +1,4 @@ { - #format : #tonel + #format : #tonel, #version : #'1.0' } From 09e6e7cac2a26c445be8e0ef82baabc7f6507bc7 Mon Sep 17 00:00:00 2001 From: Brendan Date: Mon, 19 Jan 2026 17:25:25 +0100 Subject: [PATCH 5/6] Fix distanceTo: --- src/Geometry/Collection.extension.st | 8 +-- src/Geometry/G1DElement.class.st | 14 ++--- src/Geometry/G2DCoordinates.class.st | 12 ++-- src/Geometry/G3DCoordinates.class.st | 16 +++--- src/Geometry/GAngle.class.st | 76 ++++++++++++------------- src/Geometry/GArc.class.st | 48 ++++++++-------- src/Geometry/GCircle.class.st | 20 +++---- src/Geometry/GCoordinates.class.st | 68 +++++++++++----------- src/Geometry/GElement.class.st | 38 ++++++------- src/Geometry/GEllipse.class.st | 66 +++++++++++---------- src/Geometry/GLine.class.st | 64 ++++++++++----------- src/Geometry/GMatrix.class.st | 24 ++++---- src/Geometry/GPoint.class.st | 58 +++++++++---------- src/Geometry/GPolygon.class.st | 48 ++++++++-------- src/Geometry/GRay.class.st | 40 +++++++------ src/Geometry/GRectangle.class.st | 26 ++++----- src/Geometry/GSegment.class.st | 50 ++++++++-------- src/Geometry/GShape.class.st | 26 ++++----- src/Geometry/GTriangle.class.st | 24 ++++---- src/Geometry/GVector.class.st | 44 +++++++------- src/Geometry/GZeroVector.class.st | 14 ++--- src/Geometry/GeometryError.class.st | 8 +-- src/Geometry/GeometryObject.class.st | 16 +++--- src/Geometry/Number.extension.st | 20 +++---- src/Geometry/Point.extension.st | 6 +- src/Geometry/TGWithCoordinates.trait.st | 26 ++++----- src/Geometry/package.st | 2 +- 27 files changed, 408 insertions(+), 454 deletions(-) diff --git a/src/Geometry/Collection.extension.st b/src/Geometry/Collection.extension.st index 3a4b6de..b9c0136 100644 --- a/src/Geometry/Collection.extension.st +++ b/src/Geometry/Collection.extension.st @@ -1,16 +1,16 @@ -Extension { #name : 'Collection' } +Extension { #name : #Collection } -{ #category : '*Geometry' } +{ #category : #'*Geometry' } Collection >> asGCoordinates [ ^ GCoordinates withCollection: self ] -{ #category : '*Geometry' } +{ #category : #'*Geometry' } Collection >> asGPoint [ ^ GPoint withCollection: self ] -{ #category : '*Geometry' } +{ #category : #'*Geometry' } Collection >> asGVector [ ^ GVector withCollection: self ] diff --git a/src/Geometry/G1DElement.class.st b/src/Geometry/G1DElement.class.st index 25a0602..4a93294 100644 --- a/src/Geometry/G1DElement.class.st +++ b/src/Geometry/G1DElement.class.st @@ -5,24 +5,22 @@ Description I am the common superclass to all 1D elements. A 1D elements covers elements such as points, lines, segments, rays... " Class { - #name : 'G1DElement', - #superclass : 'GElement', - #category : 'Geometry-Elements', - #package : 'Geometry', - #tag : 'Elements' + #name : #G1DElement, + #superclass : #GElement, + #category : #'Geometry-Elements' } -{ #category : 'testing' } +{ #category : #testing } G1DElement class >> isAbstract [ ^ self = G1DElement ] -{ #category : 'testing' } +{ #category : #testing } G1DElement >> boundaryContains: aPoint [ ^ self includes: aPoint ] -{ #category : 'accessing' } +{ #category : #accessing } G1DElement >> length [ "Return the length of the element." diff --git a/src/Geometry/G2DCoordinates.class.st b/src/Geometry/G2DCoordinates.class.st index b456dde..79929a6 100644 --- a/src/Geometry/G2DCoordinates.class.st +++ b/src/Geometry/G2DCoordinates.class.st @@ -14,19 +14,17 @@ Examples G2DCoordinates x: 1 y: 2 " Class { - #name : 'G2DCoordinates', - #superclass : 'GCoordinates', - #category : 'Geometry-Core', - #package : 'Geometry', - #tag : 'Core' + #name : #G2DCoordinates, + #superclass : #GCoordinates, + #category : #'Geometry-Core' } -{ #category : 'accessing' } +{ #category : #accessing } G2DCoordinates class >> numberOfDimensions [ ^ 2 ] -{ #category : 'instance creation' } +{ #category : #'instance creation' } G2DCoordinates class >> x: aNumber y: anotherNumber [ ^ self new x: aNumber; diff --git a/src/Geometry/G3DCoordinates.class.st b/src/Geometry/G3DCoordinates.class.st index 58add6c..b007a2a 100644 --- a/src/Geometry/G3DCoordinates.class.st +++ b/src/Geometry/G3DCoordinates.class.st @@ -14,19 +14,17 @@ Examples G2DCoordinates x: 1 y: 2 z: 3 " Class { - #name : 'G3DCoordinates', - #superclass : 'GCoordinates', - #category : 'Geometry-Core', - #package : 'Geometry', - #tag : 'Core' + #name : #G3DCoordinates, + #superclass : #GCoordinates, + #category : #'Geometry-Core' } -{ #category : 'accessing' } +{ #category : #accessing } G3DCoordinates class >> numberOfDimensions [ ^ 3 ] -{ #category : 'instance creation' } +{ #category : #'instance creation' } G3DCoordinates class >> x: aNumber y: anotherNumber z: thirdNumber [ ^ self new x: aNumber; @@ -35,12 +33,12 @@ G3DCoordinates class >> x: aNumber y: anotherNumber z: thirdNumber [ yourself ] -{ #category : 'accessing' } +{ #category : #accessing } G3DCoordinates >> z [ ^ coordinates at: 3 ] -{ #category : 'accessing' } +{ #category : #accessing } G3DCoordinates >> z: aNumber [ ^ coordinates at: 3 put: aNumber ] diff --git a/src/Geometry/GAngle.class.st b/src/Geometry/GAngle.class.st index 22feae8..feeccff 100644 --- a/src/Geometry/GAngle.class.st +++ b/src/Geometry/GAngle.class.st @@ -45,8 +45,8 @@ Internal Representation and Key Implementation Points. " Class { - #name : 'GAngle', - #superclass : 'GeometryObject', + #name : #GAngle, + #superclass : #GeometryObject, #instVars : [ 'radians' ], @@ -56,17 +56,15 @@ Class { 'TwoPi', 'ZeroAngle' ], - #category : 'Geometry-Core', - #package : 'Geometry', - #tag : 'Core' + #category : #'Geometry-Core' } -{ #category : 'instance creation' } +{ #category : #'instance creation' } GAngle class >> degrees: anInteger [ ^ self radians: anInteger degreesToRadians ] -{ #category : 'class initialization' } +{ #category : #'class initialization' } GAngle class >> initialize [ TwoPi := 2π. ZeroAngle := 0 rads. @@ -74,49 +72,49 @@ GAngle class >> initialize [ StraightAngle := 1π rads ] -{ #category : 'instance creation' } +{ #category : #'instance creation' } GAngle class >> radians: aNumber [ ^ self new initializeWith: aNumber; yourself ] -{ #category : 'instance creation' } +{ #category : #'instance creation' } GAngle class >> rads: aNumber [ ^ self radians: aNumber ] -{ #category : 'arithmetic' } +{ #category : #arithmetic } GAngle >> * anInteger [ ^ anInteger multiplyWithAngle: self ] -{ #category : 'arithmetic' } +{ #category : #arithmetic } GAngle >> + aGAngle [ ^ aGAngle addWithAngle: self ] -{ #category : 'arithmetic' } +{ #category : #arithmetic } GAngle >> - aGAngle [ ^ aGAngle substractWithAngle: self ] -{ #category : 'arithmetic' } +{ #category : #arithmetic } GAngle >> / anInteger [ ^ anInteger divideWithAngle: self ] -{ #category : 'comparison' } +{ #category : #comparison } GAngle >> < aGAngle [ ^ self rads < aGAngle rads ] -{ #category : 'comparison' } +{ #category : #comparison } GAngle >> <= aGAngle [ ^ (self > aGAngle) not ] -{ #category : 'comparing' } +{ #category : #comparing } GAngle >> = anObject [ "Answer whether the receiver and anObject represent the same object." @@ -126,99 +124,99 @@ GAngle >> = anObject [ ^ (radians - anObject rads) =~ 0 or: [ (radians - anObject rads) =~ TwoPi ] ] -{ #category : 'comparison' } +{ #category : #comparison } GAngle >> > aGAngle [ ^ aGAngle < self ] -{ #category : 'comparison' } +{ #category : #comparison } GAngle >> >= aGAngle [ ^ aGAngle <= self ] -{ #category : 'arithmetic' } +{ #category : #arithmetic } GAngle >> adaptToNumber: anInteger andSend: aString [ (#(#/ #+) anySatisfy: [ :forgivenSelector | aString = forgivenSelector ]) ifTrue: [ self error: 'Cannot execute ' , aString , ' on an Integer with an angle' ]. "I think we can do better than an if here but no time." ^ self perform: aString with: anInteger ] -{ #category : 'arithmetic' } +{ #category : #arithmetic } GAngle >> addWithAngle: aGAngle [ ^ (self rads + aGAngle rads) rads ] -{ #category : 'arithmetic' } +{ #category : #arithmetic } GAngle >> cos [ ^ self rads cos ] -{ #category : 'converting' } +{ #category : #converting } GAngle >> degreeNumber [ ^ self rads radiansToDegrees ] -{ #category : 'accessing' } +{ #category : #accessing } GAngle >> explementary [ ^ (0 - self rads) rads ] -{ #category : 'comparing' } +{ #category : #comparing } GAngle >> hash [ "Answer an integer value that is related to the identity of the receiver." ^ radians hash ] -{ #category : 'initialization' } +{ #category : #initialization } GAngle >> initializeWith: aNumber [ radians := aNumber. radians := radians \\ TwoPi. radians negative ifTrue: [ radians := radians + TwoPi ] ] -{ #category : 'testing' } +{ #category : #testing } GAngle >> isAcute [ ^ self > 0 rads and: [ self < RightAngle ] ] -{ #category : 'testing' } +{ #category : #testing } GAngle >> isObtuse [ ^ self > RightAngle and: [ self < StraightAngle ] ] -{ #category : 'testing' } +{ #category : #testing } GAngle >> isReflex [ ^ self > StraightAngle and: [ self rads < TwoPi ] ] -{ #category : 'testing' } +{ #category : #testing } GAngle >> isRight [ ^ self = RightAngle ] -{ #category : 'testing' } +{ #category : #testing } GAngle >> isStraight [ ^ self = StraightAngle ] -{ #category : 'testing' } +{ #category : #testing } GAngle >> isZero [ ^ self = ZeroAngle ] -{ #category : 'arithmetic' } +{ #category : #arithmetic } GAngle >> multiplyWithAngle: aGAngle [ self error: 'Not possible to multiply an angle by another angle' ] -{ #category : 'printing' } +{ #category : #printing } GAngle >> printOn: aStream [ aStream nextPutAll: (self degreeNumber asString truncateTo: 10); nextPutAll: '°' ] -{ #category : 'converting' } +{ #category : #converting } GAngle >> radianNumber [ self @@ -227,7 +225,7 @@ GAngle >> radianNumber [ ^ self rads ] -{ #category : 'accessing' } +{ #category : #accessing } GAngle >> radians [ self @@ -236,22 +234,22 @@ GAngle >> radians [ ^ self rads ] -{ #category : 'accessing' } +{ #category : #accessing } GAngle >> rads [ ^ radians ] -{ #category : 'arithmetic' } +{ #category : #arithmetic } GAngle >> sin [ ^ self rads sin ] -{ #category : 'arithmetic' } +{ #category : #arithmetic } GAngle >> substractWithAngle: aGAngle [ ^ (aGAngle rads - self rads) rads ] -{ #category : 'arithmetic' } +{ #category : #arithmetic } GAngle >> tan [ ^ self rads tan ] diff --git a/src/Geometry/GArc.class.st b/src/Geometry/GArc.class.st index 895c2ef..958f768 100644 --- a/src/Geometry/GArc.class.st +++ b/src/Geometry/GArc.class.st @@ -40,19 +40,17 @@ Internal Representation and Key Implementation Points. " Class { - #name : 'GArc', - #superclass : 'G1DElement', + #name : #GArc, + #superclass : #G1DElement, #instVars : [ 'center', 'origin', 'angle' ], - #category : 'Geometry-Elements', - #package : 'Geometry', - #tag : 'Elements' + #category : #'Geometry-Elements' } -{ #category : 'instance creation' } +{ #category : #'instance creation' } GArc class >> center: aGPoint origin: anotherGPoint angle: aGAngle [ "Take three parameters to create a new arc: - A point representing the center of the arc (center of the circle whose bond contains the arc circle) @@ -66,7 +64,7 @@ GArc class >> center: aGPoint origin: anotherGPoint angle: aGAngle [ yourself ] -{ #category : 'instance creation' } +{ #category : #'instance creation' } GArc class >> center: aGPoint origin: anotherGPoint direction: yetAnotherGPoint [ "Take three parameters to create a new arc: - A point representing the center of the arc (center of the circle whose bond contains the arc circle) @@ -77,7 +75,7 @@ GArc class >> center: aGPoint origin: anotherGPoint direction: yetAnotherGPoint ^ self center: aGPoint origin: anotherGPoint angle: (aGPoint - anotherGPoint angleWith: aGPoint - yetAnotherGPoint) ] -{ #category : 'comparing' } +{ #category : #comparing } GArc >> = anObject [ "Answer whether the receiver and anObject represent the same object." @@ -87,48 +85,48 @@ GArc >> = anObject [ ^ origin = anObject origin and: [ center = anObject center and: [ angle = anObject angle ] ] ] -{ #category : 'accessing' } +{ #category : #accessing } GArc >> angle [ ^ angle ] -{ #category : 'accessing' } +{ #category : #accessing } GArc >> angle: anObject [ angle := anObject ] -{ #category : 'accessing' } +{ #category : #accessing } GArc >> center [ ^ center ] -{ #category : 'accessing' } +{ #category : #accessing } GArc >> center: anObject [ center := anObject ] -{ #category : 'accessing' } +{ #category : #accessing } GArc >> centralAngle [ "Alias of angle to cover the geometric names" ^ self angle ] -{ #category : 'accessing' } +{ #category : #accessing } GArc >> endPoint [ "The end point is the point at the end of the arc (opposed to the origin)." ^ self origin rotatedBy: self angle about: self center ] -{ #category : 'comparing' } +{ #category : #comparing } GArc >> hash [ "Answer an integer value that is related to the identity of the receiver." ^ origin hash bitXor: (center hash bitXor: angle hash) ] -{ #category : 'testing' } +{ #category : #testing } GArc >> includes: aGPoint [ "An arc includes a point in the angle between the vector defined by the origin and center and the vector defined by the point and the center is between 0 and the arc angle and if the distance of the point from the center is equals to the radius." @@ -136,37 +134,37 @@ GArc >> includes: aGPoint [ and: [ (aGPoint distanceTo: self center) =~ self radius ] ] -{ #category : 'intersections' } +{ #category : #intersections } GArc >> intersectionsWith: anElement [ ^ anElement intersectionsWithArc: self ] -{ #category : 'intersections' } +{ #category : #intersections } GArc >> intersectionsWithEllipse: aGEllipse [ ^ aGEllipse intersectionsWithArc: self ] -{ #category : 'intersections' } +{ #category : #intersections } GArc >> intersectionsWithLine: aGLine [ ^ aGLine intersectionsWithArc: self ] -{ #category : 'accessing' } +{ #category : #accessing } GArc >> length [ ^ self angle rads * self radius ] -{ #category : 'accessing' } +{ #category : #accessing } GArc >> origin [ ^ origin ] -{ #category : 'accessing' } +{ #category : #accessing } GArc >> origin: anObject [ origin := anObject ] -{ #category : 'comparing' } +{ #category : #comparing } GArc >> printOn: aStream [ super printOn: aStream. @@ -181,12 +179,12 @@ GArc >> printOn: aStream [ << $) ] -{ #category : 'accessing' } +{ #category : #accessing } GArc >> radius [ ^ (self origin - self center) length ] -{ #category : 'comparing' } +{ #category : #comparing } GArc >> translateBy: aGVector [ self center: self center + aGVector. self origin: self origin + aGVector diff --git a/src/Geometry/GCircle.class.st b/src/Geometry/GCircle.class.st index e46ab8e..66d4c5a 100644 --- a/src/Geometry/GCircle.class.st +++ b/src/Geometry/GCircle.class.st @@ -5,39 +5,37 @@ a * (x^2) + a * (y^2) + d * x + e * y + f = 0 " Class { - #name : 'GCircle', - #superclass : 'GEllipse', - #category : 'Geometry-Shapes', - #package : 'Geometry', - #tag : 'Shapes' + #name : #GCircle, + #superclass : #GEllipse, + #category : #'Geometry-Shapes' } -{ #category : 'instance creation' } +{ #category : #'instance creation' } GCircle class >> center: aPoint1 radius: aNumber [ ^ self center: aPoint1 vertex: aPoint1 x , (aPoint1 y + aNumber) coVertex: aPoint1 x + aNumber , aPoint1 y ] -{ #category : 'comparing' } +{ #category : #comparing } GCircle >> boundaryContains: aPoint [ ^ self radius =~ (self center distanceTo: aPoint) ] -{ #category : 'accessing' } +{ #category : #accessing } GCircle >> perimeter [ ^ 2π * self radius ] -{ #category : 'printing' } +{ #category : #printing } GCircle >> printOn: aStream [ aStream nextPutAll: ('x² + y² = {1}²' format: {self radius}) ] -{ #category : 'accessing' } +{ #category : #accessing } GCircle >> radius [ ^ self semiMajorAxisLength ] -{ #category : 'public interface' } +{ #category : #'public interface' } GCircle >> upperPoint [ ^ center x , ( center y + self radius ) ] diff --git a/src/Geometry/GCoordinates.class.st b/src/Geometry/GCoordinates.class.st index ad200be..da676d9 100644 --- a/src/Geometry/GCoordinates.class.st +++ b/src/Geometry/GCoordinates.class.st @@ -33,29 +33,27 @@ Internal Representation and Key Implementation Points. " Class { - #name : 'GCoordinates', - #superclass : 'GeometryObject', + #name : #GCoordinates, + #superclass : #GeometryObject, #instVars : [ 'coordinates' ], - #category : 'Geometry-Core', - #package : 'Geometry', - #tag : 'Core' + #category : #'Geometry-Core' } -{ #category : 'instance creation' } +{ #category : #'instance creation' } GCoordinates class >> newWithCoordinates: aCollection [ ^ self basicNew initializeWith: aCollection; yourself ] -{ #category : 'accessing' } +{ #category : #accessing } GCoordinates class >> numberOfDimensions [ ^ self subclassResponsibility ] -{ #category : 'instance creation' } +{ #category : #'instance creation' } GCoordinates class >> withCollection: aCollection [ ^ self allSubclasses detect: [ :e | e numberOfDimensions = aCollection size ] @@ -66,7 +64,7 @@ GCoordinates class >> withCollection: aCollection [ ifNone: [ self error: 'This amount of coordinated is not yet managed.' ] ] -{ #category : 'arithmetic' } +{ #category : #arithmetic } GCoordinates >> * aNumber [ "Answer a Point that is the multiplication of the receiver and arg." @@ -75,7 +73,7 @@ GCoordinates >> * aNumber [ ^ self class newWithCoordinates: (self coordinatesCollect: [ :number | number * aNumber ]) ] -{ #category : 'arithmetic' } +{ #category : #arithmetic } GCoordinates >> + otherCoordinates [ "Answer a Point that is the sum of the receiver and arg." self numberOfDimensions = otherCoordinates numberOfDimensions ifFalse: [ self error: 'For now we accept only addition of coordinates from the same dimension. We will see later if we should do more.' ]. @@ -83,7 +81,7 @@ GCoordinates >> + otherCoordinates [ ^ self class newWithCoordinates: (otherCoordinates coordinatesWith: coordinates collect: [ :number1 :number2 | number1 + number2 ]) ] -{ #category : 'arithmetic' } +{ #category : #arithmetic } GCoordinates >> - otherCoordinates [ "Answer a Point that is the substraction of the receiver and arg." self numberOfDimensions = otherCoordinates numberOfDimensions ifFalse: [ self error: 'For now we accept only substraction of coordinates from the same dimension. We will see later if we should do more.' ]. @@ -91,7 +89,7 @@ GCoordinates >> - otherCoordinates [ ^ self class newWithCoordinates: (otherCoordinates coordinatesWith: coordinates collect: [ :number2 :number1 | number1 - number2 ]) ] -{ #category : 'arithmetic' } +{ #category : #arithmetic } GCoordinates >> / aNumber [ "Answer a Point that is the division of the receiver and arg." @@ -100,7 +98,7 @@ GCoordinates >> / aNumber [ ^ self class newWithCoordinates: (self coordinatesCollect: [ :number | number / aNumber ]) ] -{ #category : 'comparing' } +{ #category : #comparing } GCoordinates >> = otherCoordinates [ "Answer whether the receiver and anObject represent the same object." @@ -112,122 +110,122 @@ GCoordinates >> = otherCoordinates [ ^ true ] -{ #category : 'converting' } +{ #category : #converting } GCoordinates >> asGPoint [ ^ GPoint coordinates: self ] -{ #category : 'converting' } +{ #category : #converting } GCoordinates >> asGVector [ ^ GVector coordinates: self ] -{ #category : 'accessing' } +{ #category : #accessing } GCoordinates >> at: anInteger [ ^ self coordinates at: anInteger ] -{ #category : 'accessing' } +{ #category : #accessing } GCoordinates >> coordinates [ ^ coordinates ] -{ #category : 'enumerating' } +{ #category : #enumerating } GCoordinates >> coordinatesCollect: aBlock [ ^ coordinates collect: aBlock ] -{ #category : 'enumerating' } +{ #category : #enumerating } GCoordinates >> coordinatesWith: aCollection collect: aBlock [ ^ coordinates with: aCollection collect: aBlock ] -{ #category : 'enumerating' } +{ #category : #enumerating } GCoordinates >> coordinatesWith: aCollection do: aBlock [ coordinates with: aCollection do: aBlock ] -{ #category : 'enumerating' } +{ #category : #enumerating } GCoordinates >> fold: aBlock [ ^ self coordinates fold: aBlock ] -{ #category : 'comparing' } +{ #category : #comparing } GCoordinates >> hash [ "Answer an integer value that is related to the identity of the receiver." ^ coordinates hash negated "We negate to not have a collision with Array" ] -{ #category : 'initialization' } +{ #category : #initialization } GCoordinates >> initialize [ super initialize. coordinates := Array new: self numberOfDimensions ] -{ #category : 'private' } +{ #category : #private } GCoordinates >> initializeWith: anArray [ anArray size = self numberOfDimensions ifFalse: [ self error: 'The provided array must have the same size than my number of dimensions which is ' , self numberOfDimensions size asString ]. coordinates := anArray ] -{ #category : 'enumerating' } +{ #category : #enumerating } GCoordinates >> inject: aSeed into: aBlock [ ^ self coordinates inject: aSeed into: aBlock ] -{ #category : 'accessing' } +{ #category : #accessing } GCoordinates >> lowerLimitsWith: otherCoordinates [ ^ self class newWithCoordinates: (self coordinatesWith: otherCoordinates coordinates collect: [ :number1 :number2 | number1 min: number2 ]) ] -{ #category : 'accessing' } +{ #category : #accessing } GCoordinates >> numberOfDimensions [ ^ self class numberOfDimensions ] -{ #category : 'copying' } +{ #category : #copying } GCoordinates >> postCopy [ super postCopy. coordinates := coordinates copy ] -{ #category : 'printing' } +{ #category : #printing } GCoordinates >> printCoordinatesOn: aStream [ aStream << $(. coordinates do: [ :number | aStream << number asString ] separatedBy: [ aStream << $, ]. aStream << $) ] -{ #category : 'printing' } +{ #category : #printing } GCoordinates >> printOn: aStream [ self printCoordinatesOn: aStream ] -{ #category : 'accessing' } +{ #category : #accessing } GCoordinates >> upperLimitsWith: otherCoordinates [ ^ self class newWithCoordinates: (self coordinatesWith: otherCoordinates coordinates collect: [ :number1 :number2 | number1 max: number2 ]) ] -{ #category : 'accessing' } +{ #category : #accessing } GCoordinates >> x [ ^ coordinates at: 1 ] -{ #category : 'accessing' } +{ #category : #accessing } GCoordinates >> x: aNumber [ coordinates at: 1 put: aNumber ] -{ #category : 'accessing' } +{ #category : #accessing } GCoordinates >> y [ "Might return an error for 1D coordinates, but do they make sense?" ^ coordinates at: 2 ] -{ #category : 'accessing' } +{ #category : #accessing } GCoordinates >> y: aNumber [ "Might return an error for 1D coordinates, but do they make sense?" diff --git a/src/Geometry/GElement.class.st b/src/Geometry/GElement.class.st index 8d1d5be..8f802dc 100644 --- a/src/Geometry/GElement.class.st +++ b/src/Geometry/GElement.class.st @@ -16,19 +16,17 @@ Public API and Key Messages " Class { - #name : 'GElement', - #superclass : 'GeometryObject', - #category : 'Geometry-Elements', - #package : 'Geometry', - #tag : 'Elements' + #name : #GElement, + #superclass : #GeometryObject, + #category : #'Geometry-Elements' } -{ #category : 'testing' } +{ #category : #testing } GElement class >> isAbstract [ ^ self = GElement ] -{ #category : 'testing' } +{ #category : #testing } GElement >> boundaryContains: aPoint [ "Should return true if the boundaries of the element contains the point. Else it should return false. @@ -37,17 +35,17 @@ GElement >> boundaryContains: aPoint [ ^ self subclassResponsibility ] -{ #category : 'testing' } +{ #category : #testing } GElement >> boundaryContainsAny: points [ ^ points anySatisfy: [ :point | self boundaryContains: point ] ] -{ #category : 'testing' } +{ #category : #testing } GElement >> boundaryContainsWhichOf: points [ ^ points select: [ :point | self boundaryContains: point ] ] -{ #category : 'testing' } +{ #category : #testing } GElement >> contains: aPoint [ "Return true if the element includes the argument but not in its boundaries. @@ -57,7 +55,7 @@ GElement >> contains: aPoint [ ^ (self includes: aPoint) and: [ (self boundaryContains: aPoint) not ] ] -{ #category : 'testing' } +{ #category : #testing } GElement >> includes: aPoint [ "I should return true if the points is contained in the element including the boundaries. Else I will answer false." @@ -65,53 +63,53 @@ GElement >> includes: aPoint [ ^ self subclassResponsibility ] -{ #category : 'intersections' } +{ #category : #intersections } GElement >> intersectionsWith: anElement [ "We should use double dispatch to find intersection points" ^ self subclassResponsibility ] -{ #category : 'intersections' } +{ #category : #intersections } GElement >> intersectionsWithArc: anArc [ ^ (self intersectionsWithEllipse: (GCircle center: anArc center radius: anArc radius)) select: [ :point | anArc includes: point ] ] -{ #category : 'intersections' } +{ #category : #intersections } GElement >> intersectionsWithEllipse: aGEllipse [ "I should return the intersection points between me and an ellipse" ^ self subclassResponsibility ] -{ #category : 'intersections' } +{ #category : #intersections } GElement >> intersectionsWithLine: aGLine [ "I should return the intersection points between me and a line" ^ self subclassResponsibility ] -{ #category : 'intersections' } +{ #category : #intersections } GElement >> intersectionsWithPoint: aPoint [ ^ (self boundaryContains: aPoint) ifTrue: [ {aPoint} ] ifFalse: [ {} ] ] -{ #category : 'intersections' } +{ #category : #intersections } GElement >> intersectionsWithPolygon: aGPolygon [ ^ (aGPolygon edges flatCollect: [ :segment | segment intersectionsWith: self ]) asSet ] -{ #category : 'intersections' } +{ #category : #intersections } GElement >> intersectionsWithRay: aGRay [ ^ (aGRay asGLine intersectionsWith: self) select: [ :point | aGRay includes: point ] ] -{ #category : 'intersections' } +{ #category : #intersections } GElement >> intersectionsWithSegment: aGSegment [ ^ (aGSegment asGLine intersectionsWith: self) select: [ :point | aGSegment includes: point ] ] -{ #category : 'transforming' } +{ #category : #transforming } GElement >> translateBy: aPoint [ "Translate the shape by a delta defined by a point." diff --git a/src/Geometry/GEllipse.class.st b/src/Geometry/GEllipse.class.st index e46a62a..afb8b88 100644 --- a/src/Geometry/GEllipse.class.st +++ b/src/Geometry/GEllipse.class.st @@ -9,19 +9,17 @@ GEllipse center: 0@0 vertex: 0@10 coVertex: 10@0 ``` " Class { - #name : 'GEllipse', - #superclass : 'GShape', + #name : #GEllipse, + #superclass : #GShape, #instVars : [ 'center', 'vertex', 'coVertex' ], - #category : 'Geometry-Shapes', - #package : 'Geometry', - #tag : 'Shapes' + #category : #'Geometry-Shapes' } -{ #category : 'instance creation' } +{ #category : #'instance creation' } GEllipse class >> center: aGPoint vertex: aGPoint2 coVertex: aGPoint3 [ | d1 d2 zero | d1 := aGPoint - aGPoint2. @@ -38,7 +36,7 @@ GEllipse class >> center: aGPoint vertex: aGPoint2 coVertex: aGPoint3 [ yourself ] -{ #category : 'comparing' } +{ #category : #comparing } GEllipse >> = anObject [ "Answer whether the receiver and anObject represent the same object." @@ -48,48 +46,48 @@ GEllipse >> = anObject [ ^ vertex =~ anObject vertex and: [ center =~ anObject center and: [ coVertex =~ anObject coVertex ] ] ] -{ #category : 'accessing' } +{ #category : #accessing } GEllipse >> area [ "pi * a * b" ^ (self semiMajorAxisLength * self semiMinorAxisLength)π ] -{ #category : 'testing' } +{ #category : #testing } GEllipse >> boundaryContains: aPoint [ "Solution from: https://math.stackexchange.com/questions/76457/check-if-a-point-is-within-an-ellipse" ^ (aPoint x - center x) squared / self semiMajorAxisLength squared + ((aPoint y - center y) squared / self semiMinorAxisLength squared) = 1 ] -{ #category : 'accessing' } +{ #category : #accessing } GEllipse >> center [ ^ center ] -{ #category : 'accessing' } +{ #category : #accessing } GEllipse >> center: anObject [ center := anObject ] -{ #category : 'accessing' } +{ #category : #accessing } GEllipse >> coVertex [ ^ coVertex ] -{ #category : 'accessing' } +{ #category : #accessing } GEllipse >> coVertex: anObject [ coVertex := anObject ] -{ #category : 'accessing' } +{ #category : #accessing } GEllipse >> encompassingRectangle [ | radiuxPoints | radiuxPoints := { vertex . (center + (center - vertex)). coVertex . (center + (center - coVertex)) } collect: #coordinates. ^ GRectangle origin: (radiuxPoints fold: [ :pt1 :pt2 | pt1 upperLimitsWith: pt2 ]) asGPoint corner: (radiuxPoints fold: [ :pt1 :pt2 | pt1 lowerLimitsWith: pt2 ]) asGPoint ] -{ #category : 'transforming' } +{ #category : #transforming } GEllipse >> fitInExtent: extent [ | scales | scales := extent coordinatesWith: self encompassingRectangle extent coordinates collect: [ :number1 :number2 | number1 / number2 ]. @@ -97,45 +95,45 @@ GEllipse >> fitInExtent: extent [ coVertex := ((coVertex - center) coordinates coordinatesWith: scales collect: [ :a :b | a * b ]) asGVector + center ] -{ #category : 'accessing' } +{ #category : #accessing } GEllipse >> foci [ ^ self majorAxis asGLine intersectionsWith: (GCircle center: center radius: self fociLocation) ] -{ #category : 'accessing' } +{ #category : #accessing } GEllipse >> fociLocation [ "c² = a² - b²" ^ (self semiMajorAxisLength squared - self semiMinorAxisLength squared) sqrt ] -{ #category : 'comparing' } +{ #category : #comparing } GEllipse >> hash [ "Answer an integer value that is related to the identity of the receiver." ^ vertex hash bitXor: (center hash bitXor: coVertex hash) ] -{ #category : 'testing' } +{ #category : #testing } GEllipse >> includes: aPoint [ "Solution from: https://math.stackexchange.com/questions/76457/check-if-a-point-is-within-an-ellipse" ^ (aPoint x - center x) squared / self semiMajorAxisLength squared + ((aPoint y - center y) squared / self semiMinorAxisLength squared) <= 1 ] -{ #category : 'intersections' } +{ #category : #intersections } GEllipse >> intersectionsWith: anElement [ ^ anElement intersectionsWithEllipse: self ] -{ #category : 'intersections' } +{ #category : #intersections } GEllipse >> intersectionsWithEllipse: aGEllipse [ "And also tested" self shouldBeImplemented ] -{ #category : 'intersections' } +{ #category : #intersections } GEllipse >> intersectionsWithLine: aGLine [ " http://www.ambrsoft.com/TrigoCalc/Circles2/Ellipse/EllipseLine.htm @@ -179,7 +177,7 @@ GEllipse >> intersectionsWithLine: aGLine [ ^ {(x1 , y1) . (x2 , y2)} asSet ] -{ #category : 'intersections' } +{ #category : #intersections } GEllipse >> intersectionsWithVerticalLine: aGLine [ " Ellipse: ((x-h)²/a²) + ((y - k)²/b²) = 1 @@ -206,27 +204,27 @@ GEllipse >> intersectionsWithVerticalLine: aGLine [ ^ {(x , y1) . (x , y2)} asSet ] -{ #category : 'accessing' } +{ #category : #accessing } GEllipse >> majorAxis [ ^ GSegment with: center + (center - vertex) with: center + (vertex - center) ] -{ #category : 'accessing' } +{ #category : #accessing } GEllipse >> majorAxisLength [ ^ self majorAxis length ] -{ #category : 'accessing' } +{ #category : #accessing } GEllipse >> minorAxis [ ^ GSegment with: center + (center - coVertex) with: center + (coVertex - center) ] -{ #category : 'accessing' } +{ #category : #accessing } GEllipse >> minorAxisLength [ ^ self minorAxis length ] -{ #category : 'accessing' } +{ #category : #accessing } GEllipse >> perimeter [ "The perimeter of an ellipse cannot be calculated easily. This method will give an approximation with Ramanujan II" @@ -237,34 +235,34 @@ GEllipse >> perimeter [ ^ (semiMajorAndMinorLength * (1 + (3 * h / (10 + (4 - (3 * h)) squared))))π ] -{ #category : 'printing' } +{ #category : #printing } GEllipse >> printOn: aStream [ aStream nextPutAll: ('((x - {1})²/{2}) + ((y - {3})²/{4}) = 1' format: {center x . self semiMajorAxisLength squared . center y . self semiMinorAxisLength squared}) ] -{ #category : 'accessing' } +{ #category : #accessing } GEllipse >> semiMajorAxisLength [ ^ self majorAxis length / 2 ] -{ #category : 'accessing' } +{ #category : #accessing } GEllipse >> semiMinorAxisLength [ ^ self minorAxis length / 2 ] -{ #category : 'transforming' } +{ #category : #transforming } GEllipse >> translateBy: aGVector [ center translateBy: aGVector. vertex translateBy: aGVector. coVertex translateBy: aGVector ] -{ #category : 'accessing' } +{ #category : #accessing } GEllipse >> vertex [ ^ vertex ] -{ #category : 'accessing' } +{ #category : #accessing } GEllipse >> vertex: anObject [ vertex := anObject ] diff --git a/src/Geometry/GLine.class.st b/src/Geometry/GLine.class.st index 2904c92..6091487 100644 --- a/src/Geometry/GLine.class.st +++ b/src/Geometry/GLine.class.st @@ -27,19 +27,17 @@ Internal Representation and Key Implementation Points. " Class { - #name : 'GLine', - #superclass : 'G1DElement', + #name : #GLine, + #superclass : #G1DElement, #instVars : [ 'v1', 'v2', 'equationCache' ], - #category : 'Geometry-Elements', - #package : 'Geometry', - #tag : 'Elements' + #category : #'Geometry-Elements' } -{ #category : 'instance creation' } +{ #category : #'instance creation' } GLine class >> a: a b: b c: c [ "ax + by + c = 0 ax + c = -by @@ -53,7 +51,7 @@ GLine class >> a: a b: b c: c [ ^ b = 0 ifFalse: [ self through: 1 , ((a * 1 + c) / b) negated and: 2 , ((a * 2 + c) / b) negated ] ifTrue: [ self through: ((b * 1 + c) / a) negated , 1 and: ((b * 2 + c) / a) negated , 2 ] ] -{ #category : 'instance creation' } +{ #category : #'instance creation' } GLine class >> through: aPoint1 and: aPoint2 [ ^ self new v1: aPoint1; @@ -61,7 +59,7 @@ GLine class >> through: aPoint1 and: aPoint2 [ yourself ] -{ #category : 'comparing' } +{ #category : #comparing } GLine >> = line [ self == line ifTrue: [ ^ true ]. self class = line class ifFalse: [ ^ false ]. @@ -73,32 +71,32 @@ GLine >> = line [ do: [ "This can happen if b = 0" (self xFor: 1) =~ (line xFor: 1) ] ] -{ #category : 'accessing' } +{ #category : #accessing } GLine >> a [ ^ self linearEquation at: #a ] -{ #category : 'arithmetic' } +{ #category : #arithmetic } GLine >> angleWith: aLine [ ^ self v1 - self v2 angleWith: aLine v1 - aLine v2 ] -{ #category : 'converting' } +{ #category : #converting } GLine >> asGLine [ ^ self ] -{ #category : 'accessing' } +{ #category : #accessing } GLine >> b [ ^ self linearEquation at: #b ] -{ #category : 'accessing' } +{ #category : #accessing } GLine >> c [ ^ self linearEquation at: #c ] -{ #category : 'accessing' } +{ #category : #accessing } GLine >> determinantWith: aLine [ "I return the determinant between two line. @@ -111,34 +109,34 @@ GLine >> determinantWith: aLine [ ^ (GMatrix rows: {{self a . self b} . {aLine a . aLine b}}) determinant ] -{ #category : 'distance functions' } +{ #category : #'distance functions' } GLine >> distanceTo: aGPoint [ ^ (self a * aGPoint x + (self b * aGPoint y) + self c) abs / (self a squared + self b squared) sqrt ] -{ #category : 'comparing' } +{ #category : #comparing } GLine >> hash [ ^ ([ self yFor: 1 ] on: GeometryError do: [ "This can happen if b = 0" self xFor: 1 ]) hash ] -{ #category : 'testing' } +{ #category : #testing } GLine >> includes: aPoint [ ^ self a * aPoint x + (self b * aPoint y) + self c =~ 0 ] -{ #category : 'intersections' } +{ #category : #intersections } GLine >> intersectionsWith: anElement [ ^ anElement intersectionsWithLine: self ] -{ #category : 'intersections' } +{ #category : #intersections } GLine >> intersectionsWithEllipse: aGEllipse [ ^ aGEllipse intersectionsWithLine: self ] -{ #category : 'intersections' } +{ #category : #intersections } GLine >> intersectionsWithLine: aGLine [ | p q r x y determinant | determinant := self determinantWith: aGLine. @@ -154,26 +152,26 @@ GLine >> intersectionsWithLine: aGLine [ ^ { (x , y) } ] -{ #category : 'properties' } +{ #category : #properties } GLine >> isParallelTo: aLine [ "If the determinant it 0 then the lines are parallel" ^ (self determinantWith: aLine) =~ 0 ] -{ #category : 'accessing' } +{ #category : #accessing } GLine >> length [ ^ Float infinity ] -{ #category : 'accessing' } +{ #category : #accessing } GLine >> linearEquation [ "Return a dictionary with value for a, b and c representing the line with an equation of the form: ax + by + c = 0" ^ equationCache ifNil: [ equationCache := self privateLinearEquationComputation ] ] -{ #category : 'printing' } +{ #category : #printing } GLine >> printOn: aStream [ self a ~~ 0 ifTrue: [ self a ~~ 1 ifTrue: [ self a printOn: aStream ]. @@ -189,7 +187,7 @@ GLine >> printOn: aStream [ aStream nextPutAll: ' = 0' ] -{ #category : 'private' } +{ #category : #private } GLine >> privateLinearEquationComputation [ v1 y = v2 y ifTrue: [ ^ Dictionary with: #a -> 0 with: #b -> 1 with: #c -> v1 y negated ]. v1 x = v2 x ifTrue: [ ^ Dictionary with: #a -> 1 with: #b -> 0 with: #c -> v1 x negated ]. @@ -197,47 +195,47 @@ GLine >> privateLinearEquationComputation [ ^ Dictionary with: #a -> (v1 y - v2 y) with: #b -> (v2 x - v1 x) with: #c -> ((v1 x - v2 x) * v1 y + ((v2 y - v1 y) * v1 x)) ] -{ #category : 'initialization' } +{ #category : #initialization } GLine >> resetEquationCache [ equationCache := nil ] -{ #category : 'transforming' } +{ #category : #transforming } GLine >> translateBy: aGVector [ self v1: self v1 + aGVector. self v2: self v2 + aGVector ] -{ #category : 'accessing' } +{ #category : #accessing } GLine >> v1 [ ^ v1 ] -{ #category : 'accessing' } +{ #category : #accessing } GLine >> v1: aPoint [ v1 := aPoint. self resetEquationCache ] -{ #category : 'accessing' } +{ #category : #accessing } GLine >> v2 [ ^ v2 ] -{ #category : 'accessing' } +{ #category : #accessing } GLine >> v2: aPoint [ v2 := aPoint. self resetEquationCache ] -{ #category : 'properties' } +{ #category : #properties } GLine >> xFor: anY [ self a = 0 ifTrue: [ self error: 'Cannot answer a x if a = 0' ]. ^ ((anY * self b + self c) / self a) negated ] -{ #category : 'properties' } +{ #category : #properties } GLine >> yFor: anX [ self b = 0 ifTrue: [ self error: 'Cannot answer an y if b = 0' ]. diff --git a/src/Geometry/GMatrix.class.st b/src/Geometry/GMatrix.class.st index c8c2844..e6ee445 100644 --- a/src/Geometry/GMatrix.class.st +++ b/src/Geometry/GMatrix.class.st @@ -15,24 +15,22 @@ Internal Representation and Key Implementation Points. " Class { - #name : 'GMatrix', - #superclass : 'GeometryObject', + #name : #GMatrix, + #superclass : #GeometryObject, #instVars : [ 'rows' ], - #category : 'Geometry-Core', - #package : 'Geometry', - #tag : 'Core' + #category : #'Geometry-Core' } -{ #category : 'instance creation' } +{ #category : #'instance creation' } GMatrix class >> rows: aCollection [ ^ self new rows: aCollection; yourself ] -{ #category : 'comparing' } +{ #category : #comparing } GMatrix >> = anObject [ "Answer whether the receiver and anObject represent the same object." @@ -41,35 +39,35 @@ GMatrix >> = anObject [ ^ rows = anObject rows ] -{ #category : 'accessing' } +{ #category : #accessing } GMatrix >> at: anInteger [ ^ self rows at: anInteger ] -{ #category : 'accessing' } +{ #category : #accessing } GMatrix >> at: anInteger at: anInteger2 [ ^ (self at: anInteger) at: anInteger2 ] -{ #category : 'arithmetic' } +{ #category : #arithmetic } GMatrix >> determinant [ self flag: #todo. "For now this implementation is only for 2D squared matrix. We should later manage n-dimensions matrix" ^ (self at: 1 at: 1) * (self at: 2 at: 2) - ((self at: 1 at: 2) * (self at: 2 at: 1)) ] -{ #category : 'comparing' } +{ #category : #comparing } GMatrix >> hash [ "Answer an integer value that is related to the identity of the receiver." ^ rows hash negated ] -{ #category : 'accessing' } +{ #category : #accessing } GMatrix >> rows [ ^ rows ] -{ #category : 'accessing' } +{ #category : #accessing } GMatrix >> rows: anObject [ rows := anObject ] diff --git a/src/Geometry/GPoint.class.st b/src/Geometry/GPoint.class.st index ec13eda..23c072a 100644 --- a/src/Geometry/GPoint.class.st +++ b/src/Geometry/GPoint.class.st @@ -45,38 +45,36 @@ Internal Representation and Key Implementation Points. " Class { - #name : 'GPoint', - #superclass : 'G1DElement', + #name : #GPoint, + #superclass : #G1DElement, #traits : 'TGWithCoordinates', #classTraits : 'TGWithCoordinates classTrait', #instVars : [ 'coordinates' ], - #category : 'Geometry-Core', - #package : 'Geometry', - #tag : 'Core' + #category : #'Geometry-Core' } -{ #category : 'instance creation' } +{ #category : #'instance creation' } GPoint class >> coordinates: aCoordinates [ ^ self new coordinates: aCoordinates; yourself ] -{ #category : 'instance creation' } +{ #category : #'instance creation' } GPoint class >> withCollection: aCollection [ ^ self coordinates: (GCoordinates withCollection: aCollection) ] -{ #category : 'instance creation' } +{ #category : #'instance creation' } GPoint class >> x: aNumber y: anotherNumber [ ^ self new coordinates: (G2DCoordinates x: aNumber y: anotherNumber); yourself ] -{ #category : 'arithmetic' } +{ #category : #arithmetic } GPoint >> + aGVector [ "I return a new point translated by a vector" @@ -85,14 +83,14 @@ GPoint >> + aGVector [ yourself ] -{ #category : 'arithmetic' } +{ #category : #arithmetic } GPoint >> - aPoint [ "I return a vector represented by the substraction of two points" ^ (self coordinates - aPoint coordinates) asGVector ] -{ #category : 'comparing' } +{ #category : #comparing } GPoint >> = anotherPoint [ "Answer whether the receiver and anObject represent the same object." @@ -102,38 +100,38 @@ GPoint >> = anotherPoint [ ^ coordinates = anotherPoint coordinates ] -{ #category : 'comparing' } +{ #category : #comparing } GPoint >> =~ aPoint [ ^ (self distanceTo: aPoint) =~ 0 ] -{ #category : 'arithmetic' } +{ #category : #arithmetic } GPoint >> additionWithVector: aGVector [ ^ self + aGVector ] -{ #category : 'converting' } +{ #category : #converting } GPoint >> asGPoint [ ^ self ] -{ #category : 'converting' } +{ #category : #converting } GPoint >> asPoint [ ^ self x @ self y ] -{ #category : 'accessing' } +{ #category : #accessing } GPoint >> coordinates [ ^ coordinates ] -{ #category : 'accessing' } +{ #category : #accessing } GPoint >> coordinates: anObject [ coordinates := anObject ] -{ #category : 'comparing' } +{ #category : #comparing } GPoint >> distanceTo: aGPoint [ "Answer the distance between aPoint and the receiver. @@ -142,41 +140,41 @@ GPoint >> distanceTo: aGPoint [ ^ (self - aGPoint) length ] -{ #category : 'comparing' } +{ #category : #comparing } GPoint >> hash [ "Answer an integer value that is related to the identity of the receiver." ^ coordinates hash squared "Try to avoid collisions with GAbstractCoordinates" ] -{ #category : 'testing' } +{ #category : #testing } GPoint >> includes: aGPoint [ ^ aGPoint =~ self ] -{ #category : 'intersections' } +{ #category : #intersections } GPoint >> intersectionsWith: anElement [ ^ anElement intersectionsWithPoint: self ] -{ #category : 'intersections' } +{ #category : #intersections } GPoint >> intersectionsWithEllipse: aGEllipse [ ^ aGEllipse intersectionsWithPoint: self ] -{ #category : 'intersections' } +{ #category : #intersections } GPoint >> intersectionsWithLine: aGLine [ ^ aGLine intersectionsWithPoint: self ] -{ #category : 'accessing' } +{ #category : #accessing } GPoint >> length [ "https://math.stackexchange.com/questions/1936865/what-is-the-length-of-a-point-on-the-real-number-line" ^ 0 ] -{ #category : 'transformations' } +{ #category : #transformations } GPoint >> rotateBy: aGAngle [ "Rotate the point around the origin (0,0) of the plan. @@ -187,7 +185,7 @@ GPoint >> rotateBy: aGAngle [ self rotateBy: aGAngle about: 0 , 0 ] -{ #category : 'transformations' } +{ #category : #transformations } GPoint >> rotateBy: aGAngle about: aGPoint [ "Rotate the point around the another point of the plan. @@ -204,7 +202,7 @@ GPoint >> rotateBy: aGAngle about: aGPoint [ self y: sin * diffX + (cos * diffY) + aGPoint y ] -{ #category : 'transformations' } +{ #category : #transformations } GPoint >> rotatedBy: aGAngle [ "Rotate the point around the origin (0,0) of the plan. @@ -215,7 +213,7 @@ GPoint >> rotatedBy: aGAngle [ ^ self rotatedBy: aGAngle about: 0 , 0 ] -{ #category : 'transformations' } +{ #category : #transformations } GPoint >> rotatedBy: aGAngle about: aGPoint [ "Rotate the point around another point of the plan. @@ -231,12 +229,12 @@ GPoint >> rotatedBy: aGAngle about: aGPoint [ ^ cos * diffX - (sin * diffY) + aGPoint x , (sin * diffX + (cos * diffY) + aGPoint y) ] -{ #category : 'accessing' } +{ #category : #accessing } GPoint >> segment: aPoint [ ^ GSegment with: self with: aPoint ] -{ #category : 'transforming' } +{ #category : #transforming } GPoint >> translateBy: aVector [ "Answer a Point translated by a vector." diff --git a/src/Geometry/GPolygon.class.st b/src/Geometry/GPolygon.class.st index b65f0c7..22a4bf1 100644 --- a/src/Geometry/GPolygon.class.st +++ b/src/Geometry/GPolygon.class.st @@ -4,17 +4,15 @@ I'm polygon builded on my vertices. " Class { - #name : 'GPolygon', - #superclass : 'GShape', + #name : #GPolygon, + #superclass : #GShape, #instVars : [ 'vertices' ], - #category : 'Geometry-Shapes', - #package : 'Geometry', - #tag : 'Shapes' + #category : #'Geometry-Shapes' } -{ #category : 'public' } +{ #category : #public } GPolygon class >> convexHullOn: aCollection [ | lastPoint nextPoint convexHull | "self halt." @@ -37,7 +35,7 @@ GPolygon class >> convexHullOn: aCollection [ ^ self vertices: convexHull ] -{ #category : 'instance creation' } +{ #category : #'instance creation' } GPolygon class >> newRegularPolygonWithEdgeNumber: aNumber [ "I return the polygon vertices for a regular polygon with a number of segment given as parameter. @@ -49,20 +47,20 @@ GPolygon class >> newRegularPolygonWithEdgeNumber: aNumber [ ^ self newVertices: ((1 to: aNumber) collect: [ :index | ((encompassingCircleRadius + index) * angle) sin , ((encompassingCircleRadius + index) * angle) cos negated ]) ] -{ #category : 'instance creation' } +{ #category : #'instance creation' } GPolygon class >> newVertices: aCollection [ self deprecated: 'Use #vertices: instead' transformWith: '`@receiver newVertices: `@statements' -> '`@receiver vertices: `@statements'. ^ self vertices: aCollection ] -{ #category : 'instance creation' } +{ #category : #'instance creation' } GPolygon class >> vertices: aCollection [ ^ self new vertices: aCollection; yourself ] -{ #category : 'comparing' } +{ #category : #comparing } GPolygon >> = anObject [ "Answer whether the receiver and anObject represent the same object." @@ -72,12 +70,12 @@ GPolygon >> = anObject [ ^ vertices = anObject vertices ] -{ #category : 'testing' } +{ #category : #testing } GPolygon >> boundaryContains: aGPoint [ ^ self edges anySatisfy: [ :segment | segment includes: aGPoint ] ] -{ #category : 'accessing' } +{ #category : #accessing } GPolygon >> edges [ | edges | edges := (self vertices overlappingPairsCollect: [ :point1 :point2 | (GSegment with: point1 with: point2) ]) asOrderedCollection. @@ -85,7 +83,7 @@ GPolygon >> edges [ ^ edges ] -{ #category : 'accessing' } +{ #category : #accessing } GPolygon >> encompassingRectangle [ | origin corner | origin := ((self vertices collect: #coordinates) fold: [ :coor1 :coor2 | coor1 lowerLimitsWith: coor2 ]) asGPoint. @@ -93,12 +91,12 @@ GPolygon >> encompassingRectangle [ ^ GRectangle origin: origin corner: corner ] -{ #category : 'rectangle functions' } +{ #category : #'rectangle functions' } GPolygon >> expandBy: anInteger [ self fitInExtent: self extent + (2 * {anInteger . anInteger} asGVector) ] -{ #category : 'transforming' } +{ #category : #transforming } GPolygon >> fitInExtent: extent [ "I take as parameter a point and will fit the polygon in a rectangle whose dimensions is defined by the extent." @@ -107,14 +105,14 @@ GPolygon >> fitInExtent: extent [ vertices := self vertices collect: [ :vertice | ((vertice - self center) coordinates coordinatesWith: scales collect: [ :a :b | a * b ]) asGVector + self center ] ] -{ #category : 'comparing' } +{ #category : #comparing } GPolygon >> hash [ "Answer an integer value that is related to the identity of the receiver." ^ vertices hash bitXor: self class hash ] -{ #category : 'testing' } +{ #category : #testing } GPolygon >> includes: aPoint [ " Thanks to Google and Randolph Franklin i don't have to reinvent this very simple algorithm. See [ 1 ] for details, copyrights etc. @@ -137,27 +135,27 @@ GPolygon >> includes: aPoint [ ^ inside ] -{ #category : 'intersections' } +{ #category : #intersections } GPolygon >> intersectionsWith: anElement [ ^ anElement intersectionsWithPolygon: self ] -{ #category : 'intersections' } +{ #category : #intersections } GPolygon >> intersectionsWithEllipse: aGEllipse [ ^ aGEllipse intersectionsWithPolygon: self ] -{ #category : 'intersections' } +{ #category : #intersections } GPolygon >> intersectionsWithLine: aGLine [ ^ aGLine intersectionsWithPolygon: self ] -{ #category : 'accessing' } +{ #category : #accessing } GPolygon >> perimeter [ ^ self edges sum: #length ] -{ #category : 'transforming' } +{ #category : #transforming } GPolygon >> scaleBy: anInteger [ anInteger <= 0 ifTrue: [ self error: 'Scale must be over zeo' ]. @@ -165,20 +163,20 @@ GPolygon >> scaleBy: anInteger [ vertices := self vertices collect: [ :vertice | (vertice - self center) * anInteger + self center ] ] -{ #category : 'transforming' } +{ #category : #transforming } GPolygon >> translateBy: aPoint [ "Move a polygon by a delta defined by aPoint" vertices := self vertices collect: [ :point | point + aPoint ] ] -{ #category : 'accessing' } +{ #category : #accessing } GPolygon >> vertices [ ^ vertices ] -{ #category : 'accessing' } +{ #category : #accessing } GPolygon >> vertices: aCollection [ vertices := aCollection collect: #asGPoint ] diff --git a/src/Geometry/GRay.class.st b/src/Geometry/GRay.class.st index 2c15264..801d705 100644 --- a/src/Geometry/GRay.class.st +++ b/src/Geometry/GRay.class.st @@ -28,18 +28,16 @@ Internal Representation and Key Implementation Points. " Class { - #name : 'GRay', - #superclass : 'G1DElement', + #name : #GRay, + #superclass : #G1DElement, #instVars : [ 'initialPoint', 'directionPoint' ], - #category : 'Geometry-Elements', - #package : 'Geometry', - #tag : 'Elements' + #category : #'Geometry-Elements' } -{ #category : 'instance creation' } +{ #category : #'instance creation' } GRay class >> origin: aGPoint direction: anotherPoint [ ^ self new initialPoint: aGPoint; @@ -47,7 +45,7 @@ GRay class >> origin: aGPoint direction: anotherPoint [ yourself ] -{ #category : 'comparing' } +{ #category : #comparing } GRay >> = aRay [ self == aRay ifTrue: [ ^ true ]. self class = aRay class ifFalse: [ ^ false ]. @@ -55,78 +53,78 @@ GRay >> = aRay [ ^ self initialPoint =~ aRay initialPoint and: [ (self angleWith: aRay) isZero ] ] -{ #category : 'arithmetic' } +{ #category : #arithmetic } GRay >> angleWith: aGRay [ ^ (self directionPoint - self initialPoint) angleWith: (aGRay directionPoint - aGRay initialPoint) ] -{ #category : 'converting' } +{ #category : #converting } GRay >> asGLine [ ^ GLine through: self initialPoint and: self directionPoint ] -{ #category : 'accessing' } +{ #category : #accessing } GRay >> directionPoint [ ^ directionPoint ] -{ #category : 'accessing' } +{ #category : #accessing } GRay >> directionPoint: aGPoint [ directionPoint := aGPoint asGPoint ] -{ #category : 'properties' } +{ #category : #properties } GRay >> flipped [ ^ self class origin: self initialPoint direction: initialPoint + (initialPoint - directionPoint) ] -{ #category : 'comparing' } +{ #category : #comparing } GRay >> hash [ "We define the hash by its origin and the angle the ray has with the x axis." ^ self initialPoint hash bitXor: (self directionPoint - self initialPoint angleWith: {1 . 1} asGVector) hash ] -{ #category : 'testing' } +{ #category : #testing } GRay >> includes: aPoint [ ^ ((self directionPoint - self initialPoint) angleWith: (aPoint - self initialPoint)) isZero ] -{ #category : 'accessing' } +{ #category : #accessing } GRay >> initialPoint [ ^ initialPoint ] -{ #category : 'accessing' } +{ #category : #accessing } GRay >> initialPoint: aGPoint [ initialPoint := aGPoint asGPoint ] -{ #category : 'intersections' } +{ #category : #intersections } GRay >> intersectionsWith: anElement [ ^ anElement intersectionsWithRay: self ] -{ #category : 'intersections' } +{ #category : #intersections } GRay >> intersectionsWithEllipse: aGEllipse [ ^ aGEllipse intersectionsWithRay: self ] -{ #category : 'intersections' } +{ #category : #intersections } GRay >> intersectionsWithLine: aGLine [ ^ aGLine intersectionsWithRay: self ] -{ #category : 'accessing' } +{ #category : #accessing } GRay >> length [ "A ray is not finite." ^ Float infinity ] -{ #category : 'transforming' } +{ #category : #transforming } GRay >> translateBy: aGVector [ initialPoint := initialPoint + aGVector. directionPoint := directionPoint + aGVector diff --git a/src/Geometry/GRectangle.class.st b/src/Geometry/GRectangle.class.st index 9180be1..a02812a 100644 --- a/src/Geometry/GRectangle.class.st +++ b/src/Geometry/GRectangle.class.st @@ -2,14 +2,12 @@ I am a rectangle. See my parent for API " Class { - #name : 'GRectangle', - #superclass : 'GPolygon', - #category : 'Geometry-Shapes', - #package : 'Geometry', - #tag : 'Shapes' + #name : #GRectangle, + #superclass : #GPolygon, + #category : #'Geometry-Shapes' } -{ #category : 'instance creation' } +{ #category : #'instance creation' } GRectangle class >> left: left right: right top: top bottom: bottom [ "Answer an instance of me whose left, right, top, and bottom coordinates are determined by the arguments." @@ -21,7 +19,7 @@ GRectangle class >> left: left right: right top: top bottom: bottom [ ^ self origin: origin corner: corner ] -{ #category : 'instance creation' } +{ #category : #'instance creation' } GRectangle class >> origin: point1 corner: point2 [ "Answer an instance of me whose corners (top left and bottom right) are determined by the arguments." @@ -35,7 +33,7 @@ GRectangle class >> origin: point1 corner: point2 [ ^ self vertices: { or . (cor x , or y). cor. (or x , cor y) } ] -{ #category : 'instance creation' } +{ #category : #'instance creation' } GRectangle class >> vertices: aCollection [ | rectangle edges | @@ -50,27 +48,27 @@ GRectangle class >> vertices: aCollection [ ^ rectangle ] -{ #category : 'accessing' } +{ #category : #accessing } GRectangle >> area [ "Answer the receiver's area, the product of width and height." ^ self width * self height max: 0 ] -{ #category : 'accessing' } +{ #category : #accessing } GRectangle >> center [ "Answer the point at the center of the receiver." ^ self diagonals anyOne midPoint ] -{ #category : 'accessing' } +{ #category : #accessing } GRectangle >> diagonals [ ^ {((self vertices at: 1) segment: (self vertices at: 3)). ((self vertices at: 2) segment: (self vertices at: 4))} ] -{ #category : 'accessing' } +{ #category : #accessing } GRectangle >> extent [ "Answer a vector representing the extent of my encompassing rectangle." @@ -81,14 +79,14 @@ GRectangle >> extent [ ^ (encompassingRectangleOrigin - encompassingRectangleCorner) asGVector ] -{ #category : 'accessing' } +{ #category : #accessing } GRectangle >> height [ "Answer the height of the receiver." ^ (self vertices at: 2) distanceTo: (self vertices at: 3) ] -{ #category : 'accessing' } +{ #category : #accessing } GRectangle >> width [ "Answer the width of the receiver." diff --git a/src/Geometry/GSegment.class.st b/src/Geometry/GSegment.class.st index 7bffb3d..20e7b3a 100644 --- a/src/Geometry/GSegment.class.st +++ b/src/Geometry/GSegment.class.st @@ -30,18 +30,16 @@ Internal Representation and Key Implementation Points. " Class { - #name : 'GSegment', - #superclass : 'G1DElement', + #name : #GSegment, + #superclass : #G1DElement, #instVars : [ 'v1', 'v2' ], - #category : 'Geometry-Elements', - #package : 'Geometry', - #tag : 'Elements' + #category : #'Geometry-Elements' } -{ #category : 'instance creation' } +{ #category : #'instance creation' } GSegment class >> with: aPoint1 with: aPoint2 [ ^ self new v1: aPoint1; @@ -49,7 +47,7 @@ GSegment class >> with: aPoint1 with: aPoint2 [ yourself ] -{ #category : 'comparing' } +{ #category : #comparing } GSegment >> = aSegment [ self == aSegment ifTrue: [ ^ true ]. self class = aSegment class ifFalse: [ ^ false ]. @@ -58,24 +56,24 @@ GSegment >> = aSegment [ or: [ v1 =~ aSegment v2 and: [ v2 =~ aSegment v1 ] ] ] -{ #category : 'arithmetic' } +{ #category : #arithmetic } GSegment >> angleWith: aSegment [ ^ self v1 - self v2 angleWith: aSegment v1 - aSegment v2 ] -{ #category : 'properties' } +{ #category : #properties } GSegment >> asGLine [ ^ GLine through: v1 and: v2 ] -{ #category : 'properties' } +{ #category : #properties } GSegment >> distanceTo: aGPoint [ | ab ap t proj distance | distance := SmallInteger maxVal. ab := (v2 asPoint) - (v1 asPoint). - ab isZero ifTrue:[^distance]. + ab isZero ifTrue:[ ^distance ]. ap := (aGPoint asPoint) - (v1 asPoint). t := (ab dotProduct: ap) / (ab dotProduct: ab). t := (t max: 0) min: 1. @@ -89,86 +87,86 @@ GSegment >> distanceTo: aGPoint [ ] -{ #category : 'comparing' } +{ #category : #comparing } GSegment >> hash [ ^ v1 hash bitXor: v2 hash ] -{ #category : 'properties' } +{ #category : #properties } GSegment >> includes: aGPoint [ "The point is on the segment if the sum of the distance from v1 to aGPoint and the distance of a GPoint to v2 is my length" ^ (v2 distanceTo: aGPoint) + (aGPoint distanceTo: v1) =~ self length ] -{ #category : 'intersections' } +{ #category : #intersections } GSegment >> intersectionsWith: anElement [ ^ anElement intersectionsWithSegment: self ] -{ #category : 'intersections' } +{ #category : #intersections } GSegment >> intersectionsWithEllipse: aGEllipse [ ^ aGEllipse intersectionsWithSegment: self ] -{ #category : 'intersections' } +{ #category : #intersections } GSegment >> intersectionsWithLine: aLine [ ^ aLine intersectionsWithSegment: self ] -{ #category : 'properties' } +{ #category : #properties } GSegment >> length [ "The distance between two points is the length of the vector transposing v1 into v2" ^ (v2 - v1) length ] -{ #category : 'properties' } +{ #category : #properties } GSegment >> midPoint [ ^ ((v1 coordinates + v2 coordinates) / 2) asGPoint ] -{ #category : 'properties' } +{ #category : #properties } GSegment >> perpendicularBisector [ self flag: #todo. "This is only for 2D. We should rely on GPoints to be able to do it for n-dimensions." ^ GLine a: v2 x - v1 x b: v2 y - v1 y c: (v1 x * v1 x - (v2 x * v2 x) + (v1 y * v1 y) - (v2 y * v2 y)) / 2 ] -{ #category : 'printing' } +{ #category : #printing } GSegment >> printOn: aStream [ v1 coordinates printOn: aStream. aStream nextPutAll: '>-<'. v2 coordinates printOn: aStream ] -{ #category : 'transforming' } +{ #category : #transforming } GSegment >> translateBy: aGVector [ v1 := v1 + aGVector. v2 := v2 + aGVector ] -{ #category : 'accessing' } +{ #category : #accessing } GSegment >> v1 [ ^ v1 ] -{ #category : 'accessing' } +{ #category : #accessing } GSegment >> v1: aGPoint [ v1 := aGPoint asGPoint ] -{ #category : 'accessing' } +{ #category : #accessing } GSegment >> v2 [ ^ v2 ] -{ #category : 'accessing' } +{ #category : #accessing } GSegment >> v2: aGPoint [ v2 := aGPoint asGPoint ] -{ #category : 'initialization' } +{ #category : #initialization } GSegment >> vertices [ ^ { v1 . v2 } ] diff --git a/src/Geometry/GShape.class.st b/src/Geometry/GShape.class.st index dc8cb03..3cae50b 100644 --- a/src/Geometry/GShape.class.st +++ b/src/Geometry/GShape.class.st @@ -7,45 +7,43 @@ I am a common superclass for all shapes elements. I define the minimal API each elements should be able to answer to. " Class { - #name : 'GShape', - #superclass : 'GElement', - #category : 'Geometry-Shapes', - #package : 'Geometry', - #tag : 'Shapes' + #name : #GShape, + #superclass : #GElement, + #category : #'Geometry-Shapes' } -{ #category : 'testing' } +{ #category : #testing } GShape class >> isAbstract [ ^ self = GShape ] -{ #category : 'accessing' } +{ #category : #accessing } GShape >> area [ "Return the area of the shape" ^ self subclassResponsibility ] -{ #category : 'accessing' } +{ #category : #accessing } GShape >> center [ ^ self encompassingRectangle center ] -{ #category : 'accessing' } +{ #category : #accessing } GShape >> encompassingRectangle [ "I should return a rectangle on the minimum size that contains the shape" ^ self subclassResponsibility ] -{ #category : 'accessing' } +{ #category : #accessing } GShape >> extent [ "I return a vector representing the extent of the encompassing rectangle of the shape. I should be able to translate the corner of the encompassing rectangle to its origin." ^ self encompassingRectangle extent ] -{ #category : 'transforming' } +{ #category : #transforming } GShape >> fitInExtent: anExtent [ "I should transform my shape to fit in a rectangle of size 0@0 to the extent as parameter, keeping my center. Note that I should keep proportions. We could maybe implement another method to not keep proportions." @@ -53,21 +51,21 @@ GShape >> fitInExtent: anExtent [ self subclassResponsibility ] -{ #category : 'accessing' } +{ #category : #accessing } GShape >> perimeter [ "Return the perimeter of the shape" ^ self subclassResponsibility ] -{ #category : 'transforming' } +{ #category : #transforming } GShape >> scaleBy: aPoint [ "Scale the shape by a delta defined by a point." self subclassResponsibility ] -{ #category : 'properties' } +{ #category : #properties } GShape >> semiperimeter [ ^ self perimeter / 2 ] diff --git a/src/Geometry/GTriangle.class.st b/src/Geometry/GTriangle.class.st index a0f6b32..d7c2d0b 100644 --- a/src/Geometry/GTriangle.class.st +++ b/src/Geometry/GTriangle.class.st @@ -2,26 +2,24 @@ I am a triangle " Class { - #name : 'GTriangle', - #superclass : 'GPolygon', - #category : 'Geometry-Shapes', - #package : 'Geometry', - #tag : 'Shapes' + #name : #GTriangle, + #superclass : #GPolygon, + #category : #'Geometry-Shapes' } -{ #category : 'instance creation' } +{ #category : #'instance creation' } GTriangle class >> vertices: aCollection [ aCollection size = 3 ifFalse: [ self error: 'A triangle should have 3 vertices.' ]. ^ super vertices: aCollection ] -{ #category : 'instance creation' } +{ #category : #'instance creation' } GTriangle class >> with: aPoint1 with: aPoint2 with: aPoint3 [ ^ self vertices: { aPoint1. aPoint2. aPoint3 } ] -{ #category : 'properties' } +{ #category : #properties } GTriangle >> area [ | semiperimeter| semiperimeter := self semiperimeter. @@ -32,13 +30,13 @@ GTriangle >> area [ (semiperimeter - edge length) * subProduct ]) sqrt ] -{ #category : 'figures' } +{ #category : #figures } GTriangle >> circumscribedCircle [ ^ (self edges first perpendicularBisector intersectionsWith: self edges last perpendicularBisector) ifNotEmpty: [ :points | GCircle center: points anyOne radius: (self v1 distanceTo: self v2) * (self v2 distanceTo: self v3) * (self v3 distanceTo: self v1) / (4 * self area) ] ] -{ #category : 'figures' } +{ #category : #figures } GTriangle >> isDegenerate [ ^ (self v1 = self v2 or: [ self v1 = self v3 or: [ self v2 = self v3 ] ]) ifTrue: [ true ] @@ -46,17 +44,17 @@ GTriangle >> isDegenerate [ or: [ self edges second length + self edges third length = self edges first length or: [ self edges third length + self edges first length = self edges second length ] ] ] ] -{ #category : 'accessing' } +{ #category : #accessing } GTriangle >> v1 [ ^ vertices at: 1 ] -{ #category : 'accessing' } +{ #category : #accessing } GTriangle >> v2 [ ^ vertices at: 2 ] -{ #category : 'accessing' } +{ #category : #accessing } GTriangle >> v3 [ ^ vertices at: 3 ] diff --git a/src/Geometry/GVector.class.st b/src/Geometry/GVector.class.st index 0534034..8759bc4 100644 --- a/src/Geometry/GVector.class.st +++ b/src/Geometry/GVector.class.st @@ -29,19 +29,17 @@ Internal Representation and Key Implementation Points. " Class { - #name : 'GVector', - #superclass : 'GeometryObject', + #name : #GVector, + #superclass : #GeometryObject, #traits : 'TGWithCoordinates', #classTraits : 'TGWithCoordinates classTrait', #instVars : [ 'coordinates' ], - #category : 'Geometry-Core', - #package : 'Geometry', - #tag : 'Core' + #category : #'Geometry-Core' } -{ #category : 'instance creation' } +{ #category : #'instance creation' } GVector class >> coordinates: aCoordinates [ (aCoordinates coordinates allSatisfy: [ :e | e = 0]) ifTrue: [ ^ GZeroVector coordinates: aCoordinates ]. @@ -50,32 +48,32 @@ GVector class >> coordinates: aCoordinates [ yourself ] -{ #category : 'instance creation' } +{ #category : #'instance creation' } GVector class >> withCollection: aCollection [ ^ self coordinates: (GCoordinates withCollection: aCollection) ] -{ #category : 'instance creation' } +{ #category : #'instance creation' } GVector class >> x: aNumber y: anotherNumber [ ^ self coordinates: (G2DCoordinates x: aNumber y: anotherNumber) ] -{ #category : 'arithmetic' } +{ #category : #arithmetic } GVector >> * anOperand [ ^ anOperand multiplyWithVector: self ] -{ #category : 'arithmetic' } +{ #category : #arithmetic } GVector >> + anOperand [ ^ anOperand additionWithVector: self ] -{ #category : 'arithmetic' } +{ #category : #arithmetic } GVector >> / aGVector [ self shouldBeImplemented. ] -{ #category : 'comparing' } +{ #category : #comparing } GVector >> = anotherPoint [ "Answer whether the receiver and anObject represent the same object." @@ -85,17 +83,17 @@ GVector >> = anotherPoint [ ^ coordinates = anotherPoint coordinates ] -{ #category : 'arithmetic' } +{ #category : #arithmetic } GVector >> adaptToNumber: anInteger andSend: aString [ ^ self perform: aString with: anInteger ] -{ #category : 'arithmetic' } +{ #category : #arithmetic } GVector >> additionWithVector: aGVector [ ^ (self coordinates + aGVector coordinates) asGVector ] -{ #category : 'arithmetic' } +{ #category : #arithmetic } GVector >> angleWith: aGVector [ | angle | self flag: #todo. "This implementation is only for 2D vectors. Later we should support it for n-dimension vectors." @@ -106,22 +104,22 @@ GVector >> angleWith: aGVector [ ^ (GMatrix rows: {self coordinates . aGVector coordinates}) determinant > 0 ifTrue: [ angle ] ifFalse: [ angle explementary ] ] -{ #category : 'accessing' } +{ #category : #accessing } GVector >> coordinates [ ^ coordinates ] -{ #category : 'accessing' } +{ #category : #accessing } GVector >> coordinates: anObject [ coordinates := anObject ] -{ #category : 'arithmetic' } +{ #category : #arithmetic } GVector >> dotProduct: aGVector [ ^ (self coordinatesWith: aGVector coordinates collect: [ :point1 :point2 | point1 * point2 ]) sum ] -{ #category : 'comparing' } +{ #category : #comparing } GVector >> hash [ "Answer an integer value that is related to the identity of the receiver." @@ -130,26 +128,26 @@ GVector >> hash [ ^ coordHash squared + coordHash "Try to avoid collisions with GAbstractCoordinates and GPoint" ] -{ #category : 'testing' } +{ #category : #testing } GVector >> isZeroVector [ ^ false ] -{ #category : 'accessing' } +{ #category : #accessing } GVector >> length [ "https://onlinemschool.com/math/library/vector/length/" ^ (self coordinates inject: 0 into: [ :res :number | res + number squared ]) sqrt ] -{ #category : 'arithmetic' } +{ #category : #arithmetic } GVector >> multiplyWithVector: aGVector [ "We should implement vector product later." self shouldBeImplemented ] -{ #category : 'arithmetic' } +{ #category : #arithmetic } GVector >> nonOrientedAngleWith: aGVector [ "angle = cos^-1 a . b / (|a| * |b|)" diff --git a/src/Geometry/GZeroVector.class.st b/src/Geometry/GZeroVector.class.st index f74d25a..3491b33 100644 --- a/src/Geometry/GZeroVector.class.st +++ b/src/Geometry/GZeroVector.class.st @@ -5,26 +5,24 @@ Description I represent the zero vector that is a vector of length 0 and the only vector without direction. " Class { - #name : 'GZeroVector', - #superclass : 'GVector', - #category : 'Geometry-Core', - #package : 'Geometry', - #tag : 'Core' + #name : #GZeroVector, + #superclass : #GVector, + #category : #'Geometry-Core' } -{ #category : 'instance creation' } +{ #category : #'instance creation' } GZeroVector class >> coordinates: aCoordinates [ ^ self new coordinates: aCoordinates; yourself ] -{ #category : 'arithmetic' } +{ #category : #arithmetic } GZeroVector >> angleWith: aGVector [ ^ 0 rads ] -{ #category : 'testing' } +{ #category : #testing } GZeroVector >> isZeroVector [ ^ true ] diff --git a/src/Geometry/GeometryError.class.st b/src/Geometry/GeometryError.class.st index 8c3f375..e4a17b2 100644 --- a/src/Geometry/GeometryError.class.st +++ b/src/Geometry/GeometryError.class.st @@ -5,9 +5,7 @@ Description I am a common exception for the Geometry project " Class { - #name : 'GeometryError', - #superclass : 'Error', - #category : 'Geometry-Exceptions', - #package : 'Geometry', - #tag : 'Exceptions' + #name : #GeometryError, + #superclass : #Error, + #category : #'Geometry-Exceptions' } diff --git a/src/Geometry/GeometryObject.class.st b/src/Geometry/GeometryObject.class.st index 61e65f5..15c338b 100644 --- a/src/Geometry/GeometryObject.class.st +++ b/src/Geometry/GeometryObject.class.st @@ -5,29 +5,27 @@ Description I am a common superclass for all Geometry object. Concrete (points, segments, polygons...) and abstract objects (coordinates, vectors, ...). " Class { - #name : 'GeometryObject', - #superclass : 'Object', - #category : 'Geometry-Elements', - #package : 'Geometry', - #tag : 'Elements' + #name : #GeometryObject, + #superclass : #Object, + #category : #'Geometry-Elements' } -{ #category : 'error handling' } +{ #category : #'error handling' } GeometryObject class >> error [ GeometryError signal ] -{ #category : 'error handling' } +{ #category : #'error handling' } GeometryObject class >> error: aString [ GeometryError signal: aString ] -{ #category : 'error handling' } +{ #category : #'error handling' } GeometryObject >> error [ ^ self class error ] -{ #category : 'error handling' } +{ #category : #'error handling' } GeometryObject >> error: aString [ ^ self class error: aString ] diff --git a/src/Geometry/Number.extension.st b/src/Geometry/Number.extension.st index 15e12ba..44f7410 100644 --- a/src/Geometry/Number.extension.st +++ b/src/Geometry/Number.extension.st @@ -1,48 +1,48 @@ -Extension { #name : 'Number' } +Extension { #name : #Number } -{ #category : '*Geometry' } +{ #category : #'*Geometry' } Number >> , aNumber [ ^ GPoint x: self y: aNumber ] -{ #category : '*Geometry' } +{ #category : #'*Geometry' } Number >> =~ aNumber [ ^((self - aNumber) abs) < Number epsilon. ] -{ #category : '*Geometry' } +{ #category : #'*Geometry' } Number >> degrees [ ^ GAngle degrees: self ] -{ #category : '*Geometry' } +{ #category : #'*Geometry' } Number >> divideWithAngle: aGAngle [ ^ (aGAngle rads / self) rads ] -{ #category : '*Geometry' } +{ #category : #'*Geometry' } Number class >> epsilon [ ^ 0.00001 ] -{ #category : '*Geometry' } +{ #category : #'*Geometry' } Number >> multiplyWithAngle: aGAngle [ ^ (aGAngle rads * self) rads ] -{ #category : '*Geometry' } +{ #category : #'*Geometry' } Number >> multiplyWithVector: aGVector [ ^ (aGVector coordinates * self) asGVector ] -{ #category : '*Geometry' } +{ #category : #'*Geometry' } Number >> rads [ ^ GAngle radians: self ] -{ #category : '*Geometry' } +{ #category : #'*Geometry' } Number >> π [ ^ self * Float pi ] diff --git a/src/Geometry/Point.extension.st b/src/Geometry/Point.extension.st index bd004e4..a4ff9f2 100644 --- a/src/Geometry/Point.extension.st +++ b/src/Geometry/Point.extension.st @@ -1,11 +1,11 @@ -Extension { #name : 'Point' } +Extension { #name : #Point } -{ #category : '*Geometry' } +{ #category : #'*Geometry' } Point >> asGPoint [ ^ x , y ] -{ #category : '*Geometry' } +{ #category : #'*Geometry' } Point >> asGVector [ ^ GVector coordinates: (G2DCoordinates x: x y: y) ] diff --git a/src/Geometry/TGWithCoordinates.trait.st b/src/Geometry/TGWithCoordinates.trait.st index 40631c5..901b601 100644 --- a/src/Geometry/TGWithCoordinates.trait.st +++ b/src/Geometry/TGWithCoordinates.trait.st @@ -1,58 +1,56 @@ Trait { - #name : 'TGWithCoordinates', - #category : 'Geometry-Core', - #package : 'Geometry', - #tag : 'Core' + #name : #TGWithCoordinates, + #category : #'Geometry-Core' } -{ #category : 'accessing' } +{ #category : #accessing } TGWithCoordinates >> coordinates [ ^ self explicitRequirement ] -{ #category : 'accessing' } +{ #category : #accessing } TGWithCoordinates >> coordinates: gCoordinates [ ^ self explicitRequirement ] -{ #category : 'enumerating' } +{ #category : #enumerating } TGWithCoordinates >> coordinatesWith: aCollection collect: aBlock [ ^ self coordinates coordinatesWith: aCollection coordinates collect: aBlock ] -{ #category : 'enumerating' } +{ #category : #enumerating } TGWithCoordinates >> coordinatesWith: aCollection do: aBlock [ self coordinates coordinatesWith: aCollection coordinates do: aBlock ] -{ #category : 'copying' } +{ #category : #copying } TGWithCoordinates >> postCopy [ super postCopy. self coordinates: self coordinates copy ] -{ #category : 'printing' } +{ #category : #printing } TGWithCoordinates >> printOn: aStream [ super printOn: aStream. self coordinates printCoordinatesOn: aStream ] -{ #category : 'accessing' } +{ #category : #accessing } TGWithCoordinates >> x [ ^ self coordinates x ] -{ #category : 'accessing' } +{ #category : #accessing } TGWithCoordinates >> x: aNumber [ self coordinates x: aNumber ] -{ #category : 'accessing' } +{ #category : #accessing } TGWithCoordinates >> y [ ^ self coordinates y ] -{ #category : 'accessing' } +{ #category : #accessing } TGWithCoordinates >> y: aNumber [ self coordinates y: aNumber ] diff --git a/src/Geometry/package.st b/src/Geometry/package.st index b8dbfec..c33ebb9 100644 --- a/src/Geometry/package.st +++ b/src/Geometry/package.st @@ -1 +1 @@ -Package { #name : 'Geometry' } +Package { #name : #Geometry } From 43c279b00314d202b71472efb6c453b798c71e48 Mon Sep 17 00:00:00 2001 From: Brendan Date: Thu, 12 Mar 2026 15:57:53 +0100 Subject: [PATCH 6/6] Add comments and test --- src/Geometry-Tests/GSegmentTest.class.st | 5 ++++- src/Geometry/GSegment.class.st | 22 +++++++++++++++------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/Geometry-Tests/GSegmentTest.class.st b/src/Geometry-Tests/GSegmentTest.class.st index 3c3c134..75b7230 100644 --- a/src/Geometry-Tests/GSegmentTest.class.st +++ b/src/Geometry-Tests/GSegmentTest.class.st @@ -44,7 +44,10 @@ GSegmentTest >> testDistanceTo [ self assert: ((GSegment with: -2 , 4 with: 2 , 4) distanceTo: -2 , 4) equals: 0. self assert: ((GSegment with: -2 , 4 with: 2 , 4) distanceTo: 2 , 4) equals: 0. self assert: ((GSegment with: 477 / 11 , (149 / 2) with: -56.73661227723915 , 143.3189209406019) distanceTo: 36819 / 1022 , (82249 / 1022)) =~ 0.7697676365059569. - self assert: ((GSegment with: 838 / 41 , (4811 / 82) with: 57583 / 914 , (56095 / 914)) distanceTo: 57.998905906959145 , 61.11074842728413) =~ (2 * 0.02757564283371476) + self assert: ((GSegment with: 838 / 41 , (4811 / 82) with: 57583 / 914 , (56095 / 914)) distanceTo: 57.998905906959145 , 61.11074842728413) =~ (2 * 0.02757564283371476). + + self assert: ((GSegment with: -2 , 4 with: 2 , 4) distanceTo: 3 , 4) equals: 1. + self assert: ((GSegment with: -2 , 4 with: 2 , 4) distanceTo: -2 , 5) equals: 1. ] { #category : #tests } diff --git a/src/Geometry/GSegment.class.st b/src/Geometry/GSegment.class.st index 20e7b3a..f03c264 100644 --- a/src/Geometry/GSegment.class.st +++ b/src/Geometry/GSegment.class.st @@ -69,17 +69,25 @@ GSegment >> asGLine [ { #category : #properties } GSegment >> distanceTo: aGPoint [ - | ab ap t proj distance | + | vectSegment vectPoint t distance closestPoint| + + "See:https://paulbourke.net/geometry/pointlineplane/ + and: https://gist.github.com/mattdesl/47412d930dcd8cd765c871a65532ffac" distance := SmallInteger maxVal. - ab := (v2 asPoint) - (v1 asPoint). - ab isZero ifTrue:[ ^distance ]. - ap := (aGPoint asPoint) - (v1 asPoint). - t := (ab dotProduct: ap) / (ab dotProduct: ab). + + "Vector from v1 to v2" + vectSegment := (v2 asPoint) - (v1 asPoint). + vectSegment isZero ifTrue:[ ^distance ]. + "Vector from v1 to P" + vectPoint := (aGPoint asPoint) - (v1 asPoint). + t := (vectSegment dotProduct: vectPoint) / (vectSegment dotProduct: vectSegment). + "Clamp t to [0, 1] to restrict to the segment" t := (t max: 0) min: 1. - proj := (v1 asPoint) + (ab * t). - distance := (aGPoint asPoint) distanceTo: proj. + "Closest point on segment to P" + closestPoint := (v1 asPoint) + (vectSegment * t). + distance := (aGPoint asPoint) distanceTo: closestPoint. ^distance