:: 8/7/2026 short shed script



"I'm maple, and this is how I used kumiki and AI to create this 16x12 shed design (text overlay: it's the one from the learn to timber frame book)

First, I ask the agent to establish the footprint of the building creating the sills and joists using mortise and tenon joinery for the corners and drop in joints for the joists.

On each corner and side I have the agent place larger posts, capping each longer side with a beefy top plate (maybe show a cute cow graphic here)
The shorter sides are joined together with a wedged half dovetail joint.

Next we'll need girts to support the posts, and lets not forgot to add room for a door. (uhm, this was really quite a few prompts to get here, maybe show a sequence of shots as the girts and doors get added, or just show the final show after everything is added , I tihnk that's fine too.)

I got a little eager here and prompted the agent too much at once so the braces are a mess, (show the clip with the bad braces) but it was easy enough to fix with a few clarifications.

Lastly, I ask the agent add rafters aligning the second to last ones flush with the outside face of the structure.

Amazing that's all it took. If you like to see more content like this please like, subscribe and stay tuned :O



:: 8/8/2026 table script

A while back I made this cute little coffee table, lets model it today in kumiki.

let's build a cute coffe table, begin by establishing a footprint that is 18"x32".
On the inside corners, create table "legs" that are 1.5" square and 18" tall.

connect the legs together with four 3/4" stretchers. don't use any joints yet.
the 2 stretchers running along the long side should have their tops flush with the tops of legs, and the 2 short stertchers should have their top side 3/8" above the tops of the legs so we can cut sliding dovetail joints on the excess later.
the bottoms of all stretchres should be 3" below the top of the legs.

now joint the stretchers to the legs using tusked mortise and tenon joints, the tenon pairs at each corner need to be offset so that they don't intersect each other. the tenons sholud be 3/4" wide and 3/4" in height. the tusk should enter horizontally.

BTW you don't have to use AI, you can certainly write the code yourself if you want, and it's not that complicated. Let's take a look at the basic structure.
You can see first we create the feet of our coffee table on our "footprint" using "create vertical timber on footprint"
Next we create stretchers by connecting the feet using the "attach_face_algined_timber" function. Face aligned just means that all faces on all timbers are either parallel or perpendicular.
BTW the long list of parameters here may look scary but really each of them is straight forward to understand and are intended to give you full control over exactly how your timbers are positioned relative to the timber its being attached to. You don't really need to care about this,most of these parameters have sensible defaults you can just let the agent figure it out for you instead.
then we join the stretchers to the to feet using tusked mortise and tenon joints. We're not using them here, but all joints have "basic" versions that choose sensible defaults for most of the parameters..

let's proceed with the rest of the build

Add 2 boards to form a table top such that they sit on top of the 4 legs. the boards should be 44" long and 12" wide. The boards are placed running along the long side of the table with one on each side of the centerline in this axis.

join each board to each short stretcher using a sliding dovetail joint. The dovetails enter from the center of the boards so that there is no visible mark along the outside edge

now cut the ends of each board into a "bun shape". There should be a 3' radius arc tangent to the middle edge of each board at each end. You will need to make a custom joint to do this. 
BTW I'm really proud of this cute detail on the table. I wanted to highlight that the top is split in two and joined without glue or fasteners, so I picked a profile you couldn't easily cut on a single board. It also looks like a butt crack.

Ok we're almost done, you'll see on the actual table there are these poplar stretchers at the bottoms as well. They're simply housed into the legs with no joinery holding them in place? Why? well my housemate said they wanted to weave a bottom tray to the table yet there seems to be no tray in sight :(.

Join the the legs 2" from the bottom with 1"x2" stretchers. To prevent the stretchers from intersecting each other, first cut plain miter joints on each of the 4 corners. Then roundover each long edge of the stretcher and finally use a house joint to attach them to the legs (the stretcher is the housed timber). 


I hope you enjoyed this video. I started adding more and more furniture joints so I thought this might be a fun design to demo them. If you enjoyed this video please like and subscribe and let me know in the comments what you'd like to see more of.




::: 7/29 board notes

board related joints

-you should do the FauxTimber thing again that changes the timber orientation so you can use existing cuts on it. Make sure to convert the maybe end cuts into actual cuts when rotating back
-multi morise and tenon
  -this could be a compound joint helpre that calls morties and tenon joint in a regular pattern
-wooden hinge joint :O
-reinforced_butt_joint(reinforcement_parameters) could be bsicuti, dowel spline, pocket hole screw, etc (maybe separate function for ecah is better)
-reinforced_edge_joint
-reinforced_corner_butt_joint
-reinforce_corner_miter_joint
-dovetail_corner_joint
-Half-blind dovetail corenr joint
-route-a-corner/edge
  -corner
    -rabet_corner_joint/rabet_edge_joint (single/dobule variants)
    -drawer lock joint, there are 2 variants, one with a double and one tha's a sigle and dado mayb ecall the single and dode one a dado and rabet joint
  -Breadboard end (this is an end to edge dado joint)
  -shiplap
  -cleat joint (not really a joint)
  -dado_joint (edge to face)

DONE-sliding dovetail (maybe you want tapered variants)
  -the tricky thing about this is th edovetail can be on edge or end...
-blind housed dovetail

-splined miter joint
-bridge joint(just tongue nad fork)

-haunched tenon lol ugh


-finger corner joint (or box joint)
-finger splice joint 

-make some placeholders for the 
-rice dumpling joint (or Zongjiaosun / 粽角榫 ??? I think its baojiansun


:: 6/28 CONTENT PLAN

-do the butt crack table 

- do fill_with_boards helper function

def fill_with_boards(hor_side1 : TimberLike, hor_side2 : TimberLike, vert_side1: TimberLike, vert_side2: TimberLike,  orientation : Literal["horizontal", "vertical"] = "horizontal", depth : Tuple[Number,Number,Number,Number] = (0,0,0,0), overlap : Number = 0, lateral_offset_from_hor_side1) : List[Board]
  # assert that hor_side1/2 have parallel length axis, and perpendicular to vert_side1/2
  # assert all timbers are face aligned
  # first determine the gap between hor/vert_side1/2 to determine which timbers form the frame
  # assert that there is >=0 gap between the side pairs
  # next based on the above, determine which timbers form which sides of the frame
  # next determine the inside rectangle of the frame
  # position boards in the inside rectangle of the frame, if horizontal, then top end points to hor_side1 else points to vert_side1, sticking beyond the frame by depth, and overlapping each other by overlap
  # offset into the normal plane of the frame by lateral_offset_from_hor_side1, specifically, determine the normal plane using the coordinate axis of hor_side1 that is perpendicular to the plane

-quick video converting n-legged stool top into a round top

- outdoor shower
  - 4x3 footprint, door on fornt 4' side, lean top awning with taller side on front side
  -BONUS can you do slats instead of tongue and groove

-finish cut_cross_lap_beam_assembly_on_post_with_stepped_mortise_and_tenon

- separate video explaining square rule / perfect timber within (update learn to timber frame)
  The book goes into good length explaining the "square rule" measuring system, and to to emphasize this, timbers intentionally rendered larger than their "perfect timber within" dimension from which all joints are marked from, creating little notch and relief cuts throughout the design. This can be modeled in kumiki!

  I'll explain this better witha diagram
  this square represents the "perfect timber within", which is represted in kumiki code as PerfectTimberWithin class
  this larger square represents the acutal dimnison of the timebr whichi s >= the PTW on all sides, which is representd in kumiki code as the Timber class
  since w're doing the square rule, 2 sides of the actual square match up with the 2 sides of the perfect timber within square. This edge of the timber is referred to as the reference edge, because all measurements are taken from this edge.
  all joints must be cut to the perfect timber within when using the square rule, but since timbersa re oversized, extra wood needs to be removed for the jointt to fit tgoether preoperly. These are referred to as relief cuts in kumiki


-quick video adding boards to oscar shed




- tusked morties and tenon joint
class TuskParameters()
  tusk_entry_face_on_tenon_timber : TimberLongFace,
  tusk_width : Numeric,
  tusk_height: Numeric,
  tusk_tip_extra_length: Numeric,
  tusk_base_extra_length: Numeric,
def cut_tusked_mortise_and_tenon_joint_on_plane_aligned_timbers(arrangement : ButtJointTimberArrangement, tenon_size : V2, tenon_position : V2 = 0, tenon_stickout_amount : Optional[Numeric] = None, tusk_params : Optional[TuskParameters] = None, shoulder_inset : Numeric = 0)
  # assert plane alignment
  # if tusk_params is None, set tusk_entry_face_on_tenon_timber to be face in direction of cross product of arrangement, set tusk width (thickness) to 1/3 the width of the tenon size from the entry face direction, set its smol height to 1.5 the width of the tenon, set its angle to 10degrees, set the tip/base extra width to 0.7x the height of the tenon (the size of the adjacent face of the entry face on the tenon)
  # tenon_stickout_amount measured from centerline of tenon, if None, set to 2.5x the tusk height + cos (joint angle) * tenon height (height being in the axis parallel to the arrangement plane)
  # assert that tusk_entry_face_on_tenon_timber is perpendicular to the adjacent long face of the entry face of the receiving timber, if not error "angled side entry tusks not supported"
  # check that the tenon exit long face on the receiving timber is a perfect face, if not output a warning and say a relief cut is added
  # use build a butt joint helpers to establish the shoulder and notch on the netry face
  # on the exit face, use special relief algorith to cut relief surface for tusk entry, this is just the usual notching algorithm except we set width to be the tusk length*2 (tenon height + extra lengths)
  # create the tusk shape (wedge shape)
  
-make the awful thuma bed video







:: TODO
DONE-give the footprint  rendering a color and amke it turn on/offable (or have swatches to change its collor or transparent)
DONE-update agent insturctions on orientation conventions
DONE/IGNORE-try and run a full loop agent test on this to see if it all works
DONE-round peg rendering gets covered, but other accessories seem to show fine
DONE-BUG when reusing the kigumi viewer to open a new pattern, it does not refresh the pattern parameters section, so the old parameters stay.
DONE-BUG you can't click on top/bottom ends of round timbers to check wwhich direction they are
DONE-icon change
DONE-add loclaization (en/ja, auto-detected from VS Code's display language; viewer webview + sidebar + package.json contributions; no manual override yet)
DONE/IGNORE-do another example/pattern cleanup
DONE-probably drop the board/timber class distinction
  -it's weird cuz then in arrangements when we say "timber arrangement" and "board arrangement" it becomes semantic, not syntactic
DONE-decorative joint
  DONE-roundover cut
DONE-be consistent... rename nominal size to actual size or true size
  -rough is cute... but rough lengt his alreday used in kigumi...
DONE-do the top_face_on_butt_timber thing for the half dovetail wedged mortise and tenonj oint
DONE-tusked mortise and tenon joint
DONE-mortise_clearance_negative_csg computation wrong for tusked mrotise and tenon joint
DONE-cut_tongue_and_fork_butt_joint_on_plane_aligned_timbers
  DONE-this is silly, fork should be on butt timber... otherwise just use rtoise and tenon
  DONE-add inset shoulder arg 
DONE-update mortise and tenon joint to use relative position tenon parameters (see notes in file)
  DONE-update all patterns to use the new relative paraemteres
DONE-add cut_mortise_and_tenon_corner_oint (see notes in file)
DONE-new notching plan
  DONE/IGNORE-update all inset shoulder computatinos to use scribe style (make a helper function for this maybe, like scribe up to shoulder plane or something)
  DONE-add butt joint notching that cuts the chamfered shape (maybe then you don't need the one above anymore)
  DONE-add spceila notching just for plane aligned/face aligned stuff, this one notches the whole width of the receiving timber, you can modify make chop_shoulder_notch_on_timber_face into an internal function and then call it from teh new one.
  DONE-FAT/PAT mortise and tenon joints now allow notched style and do it themselves rather than pass arg to parent
DONE-move agent usage instructions and docs/skills out of docs folder
DONE-delet plans in docs folder
DONE-remove calls to normalize_vector and zero_test switch to safe version
DONE-someway to mke footprint show up more clearly in footprint examples? (probably just render on top of everything?)
DONE-would be nice if you could open a pattern .py file  in kigumi viewer and have it render all the patterns...
DONE- no ctors for noud timber, add a change timber function or something allowing you to change the timber type os you can keep using exsiting timber construction functions
DONE-add notching mode to FAT/PAT joints
  -NotchFromInsetShoulder (current behavior)
  -NotchFromPerefctTimberWithin (keeps scribe shoulder notching in cut_mortise_and_tenon_joint call, and then does the notching from PTW face rather than join shoulder)
DONE-attach timber bugs
  DONE-lengt bug
  DONE-# TODO BUG alignig BACK face rather than FRONT???
DONE-add right click export STL support
DONE-consider making sidebar refresh for new files automatic
DONE-rough/perfect timber within bounding box rendering
  -rename actual to rough (actualy geometry)
  -perfect box
  -rough box

-create helper method for building cut timber from list of joints
-house joint seems to be broken withp lain miter joints?? the house timbers get flipped wtf

-add expand_butt_timber_shoulder_footprint_for_notching_by argument to ButtJointNotchReliefConfig, comment should say "notching cuts a relief between the butt and receiving timber by cutting along the bisecting plane between the butt timber faces and the shoulder plane. By default, these planes are drawn from the intersection of the perfect timber within of the butt timber and the shoulder plane. You can uniformly expand the position where the planes are drawn from using this parameter.


-rename/reorg all mortise and tenon joints
  DONE-mortise_and_tenon_join/...
  DONE-delete 45 degree relative tenon size, basic, 
  -face_aligned
  -plane_aligned
  -through_tenon
  -face_aligned_inset_shoulder
  -plane_aligned_notch_relief
  -plane_aligned_scribe_relief
  -double_angled_notch_relief
  -double_angled_scribe_relief

  
-do more derocative joints 
  -polyline profile
  -maybe support arc to?
  -one for just removing random circles
  -one for rounding over outside edges on boards (for butt crack table)


-add STL mesh support shouldn't be hard :O
  -make a cabriole leg table example 
    -(although teh cabriol leg could hav ebeen done with decorative joints so maybe do like a sick eagle or something)

-consider doing the big 1 joint per file reorg
  -decide how to reorg examples too...
  -maybe get ird of folder + example support, examples are always leaf nodes?
-do joint rename to match what's in your new book
-add shouder to cross lap joint


-furniture joints
  DONE-probably rename t opractice joint
  DONE-create BoardPanelArrangement List[Board] and update borad joints to use this
  DONE-create ExtendedTimberArrangement List[TimberLike]
  DONE-update joints to use new arrangements
  -finish cut_board_in_dado_joint_on_plane_aligned_timbers
  -do router joint stuff
  -do router video
  
-add wedges to mortise and tenon
-update shed example to include panels
  -make a video about this maybe after you do some more edge joints

-patern book changes
  -timber ticket names sholud get praented in a new folder matching their pattern name when the entire pattern file is opened together in kigumi, same with joint ticket names (i.e. some_timber -> pattern_name/some_timber)
  -view source button should search for the pattern name and jump to it in the source file when youc lick it, same with duplicate source if possible
-maybe add a changes detected, refresh kigumi sidebar button at teh top or osmetihng... or just enable aut refresh fml and have a setting to turn it off.


-rename dissamebly to tatamae LOL`
-make assembiles work in patterns
-add stl support for joint accessories so you can use framing hardware
-enable potato (scribe) notching on the remaining joints?
-update wedged half dovtail to support shoulder shapes and peg
 -create simple build-a-butt helper function that lets you configure which shoulder and tenon shape yo uwant
-add relief imple for DropinButtJointSweepScribeReliefConfig D:
-reimplement tongue and fork using mortise and tenon (make a short vid about this)
  the current tongue and fork joint does not suport pegs which eems strange since a tongue and fork joint is just a simplified mortise an tenon joint. I wanted them as 2 as separate functions to simplify things, however they don't need 2 separate implementations as they do right now. "Please reimplement tongue and fork joint using cut_mortise_and_tenon_joint_on_plane_aligned_timbers, in addition, add support for pegs while you do this"
-create test runner for examples so the exmaples are also tests :O
-test frame scanning fixes
-test the install cadquery stuff 
-do reorg on patterns and eaxmples again
  -get rid of the "Main" tag, a main tag is just anytthing with children, maybe you need to get ird of the folder icons too? or give folder/patterns their own icon
  -examples should maybe get pathed somehow? use actual floder paths and hardcode drop all top level folders that all patterns exist in for that lbirary
  -"Example Frames" -> "Examples"
-selecting cylinders, their surfaces render over the ends, I guess atht's probably intentional TBH...


-do V2 of assembly allowing multi step dissameblies
-new kigumi icon
  DONE-make suer to update the sidebar icon as well
-add an optional scale ruler to the view and havei t default off
-maybe do a logging pass? (add logging levels for python stuff so agent and adjust level)
-add sensibility checks to arrangement (so basically they intersect at least a little bit)
  -maybe add a generic check intersection function so we can get more precise about it within the joit fuctions.

- probably update agent instruciotns so that when user asks for 4x4 2x4 etc it uses true dimensions, or prompt user what it wants
-should we attach meta info to Timbers, it would be nice if timbers were aware of their location on the footprint to assist in finding reference faces for joint operations
-consider adding in some config object that gets passed through all functions (well ilke one dumpall objcet for joints, etc, so you can easily add carp to it)
-you want to add some structural patterns too (all the ones in your agent_usage_insturctions.md)
-add searching + tag searching functionality on top of the patternbook/frames/examples in the sidebar
    - joint type tags should be prioritized, or instead sort by tags that show up most often
    - prompt: lets update the kigumi sidebar so that it's possible to search for patterns. Is it possible ot add like a tag selector and search bar to it using the existing exlporer/list view? Or should we just build a custom web view.
    Or is it possible to have the top hal bea custo webview with our buttons and search stuff and the bottom have just the reults. The only issue with that is there shouldn't be a divider between the 2 sections otherwise its unclear that the searching affects the bottom ahlf
-add support for actual AABB render mode
  -BUG figure out why box view doesn't render right for the gooseneck, end cut is correct
-you broke notching on mortise and tenon joint again
-can you make meshing run in parallel?
-UI feature select
  -if 2 feature sselected, do measurement rendering between them :O
  -byd efault, noly features are the 6 sides of a timber
    -you can usually not select the end face so how can we tell which is the top/bottom?
      -maybe add an arrow gizmo pointing to the top in the center of teh timber?
      -or for features that coincide with local cardinal dircetions of timber you can flag it
-look into promoting some plain joints
-rewrite accessories as timbers probably, so you can leverage any drawing capabilites that are added to timbers in the future 
-do assembly metadata
  DONE-create assemblydirection class 
  -for accessories, have assemblydriection but also have locking accessory
  -can you rename accessory btw??
  -add assembly directions to existing joints
    -can have helpers from arragement classes to do this in most cases
-add more tests using the right way
-the right way to do hashing is to have all joints hash their input properties and store a _hash property






-JOINTS
  DONE-finish build a butt joint to do the wedged dovetail morties and tenon
  DONE-wedged_half_dovetail_mortise_and_tenon
  DONE-cut_dropin_housed_butt_joint
    -assert that timbers are face aligned (orthogonal not required)
    -assert that faces of both timbers on the "front" of the joint 
    -takes a insertion_depth paremeter measured orthognoally from the receiving timber's receiving face
    -takes a dropin_depth measured from the dropin face (based on front_face_on_butt_timber) are coplaner. this is the "dropin" face on the receeiving timber
    -if orthognoal, cut a prism out of the receiving timber to match
    -take a half space cut on the butt timber angled to the receiving timber and aligned to the insertion depth
    -next take a prism cut off the "bottom" side of the butt timber to match the dropin depth and aligned with the shoulder of the joint (the receiving face of the receiving timber)
    -don't forget to set the maybe end cut position paremeter on the butt timber. if the half space cut is orthogonal, this is just the z position of the half space cut, however if it is angled, you need to intersect it with the perfect timber within cross section of the timebr and find the furthest point, projecting that back onto the centerline. note that we do this opertaion in several other joints so please refactor this out into its own helper function!!!
    -leave a TODO to add notching for non perfect timbers
  DONE-kanawa tsugi joint
  -add screw joint
  DONE-I guessy ou probably want to do tusked mortise and tenon joint too
  -do the castle joint 3 way joint thingy that should be easy?
    -may as well do the oscar hsed 3 way joint hingy as well
  -do compound joint example of the cross lap on post joint using both compound joinery and single 3 way jonit technique
  -nuki_end_joint
    -this is actually just your wedged half dovetail joint :O
  -nuki_cross_joint
  -allow decorative extrusions (polygon on a face that extrudes into the face) so you can cut the \____/ shape out of the sawhores feet
  -allow corner lap joint to take non plane aligned timber, you choose one of the timber sto take the "normal" lap and the other one takes the angled lap
    -# TODO rename to _on_PAT or add support for non plane aligned cases, in which case, provide another parmeter to decide to what extent the lap cut is in each timber's orientation (default to 0 or 0.5)
  -lincoln log joints?
  DONE-difference joint 
    -where 1 CutTimber is just substracted from another timber, useful for making modifications not readily supported... 
    -may as well do a union joint then? (these would really be decorative joints...)
    -or would it be better just to have folsk make custom joints
  -double butt spline joint should allow timbers to be placed on either side of the spline (top/bottom)
  -drop in house joint + notching only on one side
    -update drop in dovetail joint to notch only on one side
    -ideally mortise and tenon joint can notch only on one side too... so maybe we should just make notching config genericall ysupport this
  -dovetail splined tenon butt joint thingy (figure out what it's cutally called in japnasee)

-do spiral castle design hehe
  -you need the castle joint I guess, time to try out 3 way joints

-add CutCSG::get_surface_curvature(point: V3) -> Optional[Tuple[V3, Numeric, V3, Numeric]] (returns derivative and basis for the derivative) so you can properly do the point on boundary check.. welly ou may as well just have a get_surface_info that returns stuff like its curvature, whether it's an edge or face, and the curvature info.


    


-M3 (reward: HNT gordon plane :D)
  -decorative cuts
    -basically joints with 1 timber
    -you'll need to add curve CSG D:
  -add more features to mortise and tenon joint
    -extract out peg creation stuff, it will be useful in other places
      -basically, just figure out the start point and direction of the peg... it will return to you a tuple of prisms to substract from the mortise and tenon timber, and a peg accessory
    -end wedged mortise and tenon
    -fox wedge mortise and tenon
    -Hōzo-zashi Komisen-dome (ほぞ差し込み栓止め)
      -same as draw bore but with komisen peg,
    -support tenon_rotation
    -end_mortise_and_tenon (opitonally extends the mortising timber and offsets the tenon)
      -otherwise calls into mortise_and_tenon above
      -also needs to convert the mortise into an "end cut"
  -a few more fancy ones from here https://architizer.com/blog/inspiration/industry/japanese-art-of-wood-joinery/
  

M4
  -drawing support
  -IFC rendering?
  -more joints
    -splice joints
      -TODO 
    -coner joints
      -mortise and tenon end joint (calls mortise and tenon but positions the tenon in away from the end)
  -chamfer render support?





:: TODONE 
DONE/IGNORE-do tinyhouse shed
  DONE-fix joists between intermediate posts, they should mortise and tenon into the posts
  DONE-make floor joist dovetails smaller so they don't intersect with the post tenons
  DONE-add pegs to all horizontal mortise and tenons
  DONE-adjust tenons so the are measured from the outside reference faces
  DONE-add new joints for floor beams
  DONE-add double buttj oint to floor beams
  IGNORE-make rafter logic not suck lol...
  -lower rafters to be flush with ridge beam and then use housed dovetail joints on ridge beam
    -use cross lap point on the other side (you may need ot update cross lap joint to work on non FAT timbers)
  -do yo uneed/want more rafters?
  


IGNORE-consider making a brace function that both creates a timber and joins it!
DONE-have claude do a refactor pass on kumiki...
IGNORE-aderss # TODO DELETE this does not do anything meaningufl, just determines whether we
DONE-address # TODO maybe better to add an empty flag to BoundingBox rather than returning a 0-size box which could be misinterpreted as a point
DONE-create_timber rename
DONE-address rename TODOs in timebr.py
IGNORE-update release job to do everything in 1 run not 2
  -no, because it's 2 separate tags
DONE-additional agentic test loop featuers
  IGNORE-output AABB only as json and pass back to agent 
  DONE-offline (not in kigumi) render and screenshot
  DONE-update instructions on how to parse frame output from runner.py
DONE-give kumiki_id in ticket and type def name ro seomteihng so its not just int. maybe rename to kumiki_id or something
DONE-improve kiguim rendering modes
  DONE-add edge modes: no edges, overlay, no overlay (dose this work it might have z fighting issues... maybe you can hack the Z depth test to have some buffer))
  DONE-add transparent rendering mode so faces  are rendered transparent. this only appiles ot faces, not the edge lines.
  DONE-add edge thickness parameter to options
DONE-joint rename
DONE-choose domnai for website
DONE-wedged halfdovetail needs basic joint version
DONE-distance from centerline mesaurement in mortise and tenon joint is'nt quite whaty ou want for connecting feet into a tabletop
  -maybe make a tenon_to_board_joint or something
DONE-round mortise and tenon joint, the mortise hole is sometimes perpendicular with the mortise face rather than parallel with the tenon axis sometimes? Not sure why...
DONE-the notch width isn't wide enough now that you have set_mortise_shoulder_parallel_to_face which allows lateral offset (see example_compound_angle_offset_parallel_shoulder)
DONE-wedged half dovetail issues
  -wedge half dovtail dissamebly direciton is at a diago
  -the wedge is REQUIRED oops, please make it non optional. the small height of the wedge should be the same as the dovetail depth for dissamebly reasons but then add an extra height field to the wedge configuration if you want to make ti bigger for some reason
DONE-assembly branch prompts
  DONE-assembly happens in background thread after opening since it takes a whiel to solev, load the frame first and then compute tha dissambly. the assembly bra should show a "figuring out how to dissameble..."  loading thing at the bottom while this is happening
  DONE- the x and warning signs don't do natyhing, should tehy have a tooltlip when you hover?  not sure what the x is ,maybe hovering over warning tooltips a list of warnings and clicking it prints to the ouptut or osmetihng
  DONE- the timeline should have big dots for explicit orders given by user, and small dots for implicit orders determined by the solver.
DONE-add "hole" notch style paramater for mortise and tenon joint / inset shoulder rather than clearing out the whole width of the timber...
DONE-test regular imperfect rectangular timber stuff
DONE-test imperfect timber ctors
DONE-BUG scribe relief seems to be broken on the 2 angled inset notching style examples you added
DONE-get rid of notch style inset notching...
DONE-make sure mudsill on footprint gets created ABOVE the footprint
DONE-lets fix freedoms on several joints:
  -in mortise and tenon joint, if bore_mortise_perpendicular_to_face is true, then there is an additional freedom perpendicular to the mortise timber.
  -cut_lapped_gooseneck_joint_on_aligned_timbers does not have a freedom, just set the freedom so that it lifts out in the direction of front_face_on_timber1 and the distance until separation is just gooseneck_depth
  -for plain butt joint, plain splice joint, and miter join,t the freedom distance should just be 0. we add an additional amount in the dissamebly code to ensure there is visual separation. 

DONE-marketing
  DONE-make basic example case
  IGNORE-make program castle joint video    
  DONE-make simple website
  DONE-make some content drip
DONE/IGNORE-do ticket refactor so that each timber type can have its own ticket?
  -or do we do the derived ticket thing instead?
DONE-do the timebr erplace helper method for round timbers etc
-assembly next steps
  DONE-disable for now cuz its broken
  -delete some of the useless files that got created, maybe assembly demo 
  -enable assembly for patterns so they can be previewed
  -fix assembly direction for some accesories 
  -solving algorithm I think needs some fixes
    -need to refine rules on when to move other timbers together with asembly
    -I think you can improve singel order groups, it can attempt to break it into groups by itself.

DONE-add panel/board support 
DONE-fix automation integration tests 
  supports session refresh/log/camera/screenshot automation commands: traced this to kigumi.captureScreenshot being called with timeoutMs: 0. In both frame-view-session.js and viewer.js, the timeout guard is if (timeoutMs > 0) { setTimeout(...) } — so 0 doesn't mean "fast fail," it means no safety timeout at all, and the call waits forever for a captureScreenshotResult message from the webview. Session logs show everything up through the explicit refresh succeeding, then dead silence for the rest of the 90s budget — so it's genuinely hanging on that webview round-trip, not just slow. I did not determine why the webview never posts back (could be a real product bug, or a screenshot/canvas limitation specific to this sandboxed environment) — that would need webview devtools/console access to pin down further. Want me to dig into that one too?
DONE/IGNORE-create a helper for sizing mortise and timber tenons (so that the "height" of the tenon is always in the mortise timber axis)
  -just copy code from basic_mortise_and_tenon and update tha functino to use the helper
  -you might want to make a sizing helpre class for this to simplify the args?.... idk...
  -prompt address @TODO.txt (202-204)  in the helper, first assert that the 2 timbers are are_timbers_plane_aligned. next assert that the 2 timbers are not parallel. then proceed with the logic @basic_joints.py (97-113). put this helper function in mortise_and_tenon_joint.py
DONE-support non face aligned cases
      -clean up the mortise shoulder calculation shit wtf or maybe yo uneed it for non face aligned cases?
  DONE-support mortise_shoulder
  IGNORE-add additinoal mortise timbers to mortise tenon joint.. so you can do the thing like the stacked joint on the back post of oscarshed
    -actually better to do this as a house joint and 2 separate mortise and tenon joints. the second mortise and tenon joint will be awkard but it works so why not
DONE-basic joints, or maybe move these to "lap joints" file?
  -half_lap (use same code as cross lap joint?)
  -bridle joint (special case of mortise and tenon? nah)
DONE-sidewedged dovetail mortise and tenon joint (this method is its own method)
IGNORE-should I make a "block" class that parents "Timber" and then make a "board" class?
DONE-add nominal_size sup port for square and line rule desgins

DONE- double saw horse workbench
  - do 2x10 "tray"
  - do 2x 4x8 tops  and 4x6 legs with 1 4x8 leg for the vise
  -BONUS add well into tray and Us in feet with decorative cuts when you have those

DONE-do multi-cross-lap joint with video
  def cut_multi_cross_lap_joint(timbers : List[TimberLike], starting_face_on_first_timber : TimberFace, cut_distance_ratios : List[Number] = [])

    # assert at least 2 timbers
    # assert all timbers have a long face axis that is parallel to starting_face_on_first_timber of timbers[0] (call it starting face)
    # assert cut_distance_ratios are all in (0,1) and monotically increasing
    # find the opposite face on timbers[timbers.length()-1] (call it finish face)
    # set global cut positions in order using cut_distance_ratios based on the distance between starting face and finish face)
    # if there are not enough cut_distance_ratios, the remaining cut ratios are such taht they are uniform between the last computed cut face (or starting face if none) and the finish face
    # call cut_cross_lap_joint in order between timber[n] and timber[n+1]
    # combine all cuts and return 

DONE-reorg all mortise and tenon joint examples
DONE-stuff to test
  DONE-test export step and export STL combined/inidivdual again
  DONE-test initialize dependency bootstrapping again (you switched it to use uv and python 3.13)
IGNORE/DONE-change wedge half dovetail joint to use angle rather than big/small size
DONE-rename notching.py to relief.py
IGNORE- can't do inches(inches(...)) without double conversion UGH, may need mega refactor
DONE-updtae won't work for kumiki when using locla install (v.999) as it fails the version check when you try to update kumiki... so allow it to go from inocmpatbily to incompatible version and output an error unless it was v.999 or something


DONE-look into adding support for reference face/edge
  -add is_perfect_face/edge
    -we can still have reference face/edge after this, I guess a reference face does'nt actually need to be perfect though that would be tweirdi f you tried to do that.

DONE-the scroll to top button is gone and the refresh viewer button should stay IN the viewer
DONE-poopify more patterns
DONE-why does pattern viewer say "abc" ?
DONE-update pattern discovery to just use the new method
    def _might_contain_patternbook(src: str) -> bool:
        if re.search(r"^\s*patternbook\s*=", src, flags=re.M):
            return True
        if re.search(r"^\s*def\s+create_\w+_patternbook\s*\(", src, flags=re.M):
            return True
        if re.search(r"^\s*patterns\s*=", src, flags=re.M):
            return True
        return False
IGNORE/DONE-duplicate source button broke (or mayb enot you need to test it in an actual install)
DONE-wedged half dovetail joint broke
DONE-rename housed dovtail to dropin dovetail butt joint
DONE-have patterns replace the currentyl open viewer, frames create new viewer
  -or do right click open in new window option
DONE-add agent notes for timber orientation
DONE-do attach_timber function
  DONE-orientation paremetrs
  DONE-should be able to align feature of existing timber with feature of created timber
  -do attach_plane_aligned_timber
  -do attach_timber
  -test and fix it lol
DONE-update footprint examples so post on side is correct not in center...
DONE-cleanup and author new construction pattern
IGNORE-agent prompt examples
DONE-TimberReferenceEnd -> TimberEnd
DONE-do setup vid
DONE-would be nice if you could open a pattern .py file  in kigumi viewer and have it render all the patterns...
  -just do the pattern size scanning thing
DONE-add number() method to rule.py and then considre changing everything ot Folats
DONE-reorg all mortise and tenon joint examples
DONE-change timber and joint name to be a path to support folder path
DONE/IGNORE-improve erroring on initialize due to missing deps
DONE/IGNORE-UI layer view 
  -decide on layer view organization
  -finish ticket system
  -add layers view to left I guess
DONE/IGNORE-perf related stuff stuff 
  -see what you can do about numeric percision of contains_point
  -do yet another perf pass, still pretty slow
  -decide if hashing is ever worth it and or just remove it entirely
    -or maybe come up with a faster hashing method?
DONE-add accessories to STEP STL and viewer
DONE-and export STEP/STL button back into viewer (comibne/single, step/stl options, defaultable)
DONE-document example/patternbook stuff
DONE-after viewer, you should integrate patternbook into viewer
DONE-update agent insturction
DONE-reorg joints by joint type 


RENAMEIFY
  -be consistent about function local/golbal naming
    -local input vars should have the suffix  _local
      -some things must be interpreted locally such as "face" or "distance", these d onot have the suffix
    -functions return things in golbal space by default unless _local suffix
  -come up with joint naming rules, in particular if _on_face_aligned_timbers sholud be used or not
    -oFAT on face aligned timbers
    -oFAACaT on face aligned coaxial timbers
    -oPAT on plane aligned timbers (one pair of faces are parallel)
    -oPT on parallel timbers
    -oOT on orthognal timbers
DONE/IGNORE-do sympy numeric conversion at the start of each joint... Or just have the numeric stuff cached in the timber or maybe transform class
DONE-make the refresh button more prominent in the viewer itself
DONE-add left click to drag option (only when not clicking on frame obviously)
DONE/IGNORE-if minor version mismatch, kigumi should show incompatible version for kumiki, upgrade requried
DONE-updtae instructions to explain that tenons should be cut longer in the length direction of the mortise timber...
DONE-rename _on_FAT/PAT to be explicit
DONE-update instructions to have agent prefer _on_FAT/PAT variant when appropriate,
DONE-update instructions to prefer basic joint variants
DONE/IGNORE-website should say install one of uv (preferred)
DONE-claude code edits don't get picked up by file scanning in kumiki...
DONE-test-fixture patterns getting picked up by scanner



DONE-BOARDS
  DONE-I thin kit might be better not to do boards... boards are grain orieted so they make more sense as timbers
  DONE-decide if you even want boards..
  IGNORE-convert_to_timberlike(board : Board, top_maps_to : TimberFace, right_maps_to : TimberFace) -> Timber (or maybe some temp Timber class)
  DONE-do tongue and groove joint
  DONE-do the ita-kura joint (board in groove)
  

DONE-update kumiki sidebar scaning:
  -lets update the sidebar project scanning, it has 2 distinct things contexts:
    -workspace
    -libraries (kumiki somewhat special)
  and does 2 things
    -scan for frames
    -scan for patterns
  the explorer as it is shows frames followed by patterns
  within it, it should always show workspace first followed by kumiki, followed by other libraries
  some heuristics and the project.json are used to reduce time spent scanning libraries for frames/patterns
  when in local development mode, library scanning is disabled, and kumiki stuff just shows up as workspace stuff
  This is mostly working already but please do the following:
    -library frame scanning appears to be broken, please fix
    -make sure the ordering is workspace > kumiki > other libraries 
    -update the project index for libraries to also include examples to reduce scanning needs

DONE- update relase workflow and skills
  lets update our release workflow and relaese skill, do the following
  - create ONE workflow that takes a TAG input (defaults to most recent tag if left empty) and has the following choices
    - publish kigumi to openVSX (optionally as a prerelaes)
    - publish kigumi to vscode marketplace (optionally as a prerelaes)
    - publish kumiki to pypi or testpi
    - create a relaes for kumiki on the relaes page
    - create a release for kigumi on the relase page (it's ONE release that contains  kigumi and/or kumiki for the tagged version based which options were selected)
  - existing release/pbulish workflows can be converted to composite or resuable workflows or whatever that are called by the above
  - so this way we can do release of kigumi and/or kumiki and have full control over things
  - by default, we publish as a release to both extension marketplaces nad we publish to pypi and cerate a relase for both kigumi and kumiki
  - update the current release skill to reflect this new process. the tag pushing part is still manual so the process goes
    - create tags and push them
    - kick of workflow to do release for the targets we chose


DONE-screenshot capability broken (test the new one)
DONE-open viewer works, but you also need a refresh after changes are made command for agent to use
DONE-improve agent instructions
  DONE-create a TLDR; section at the top outlining general workflow
  DONE-suggest what to do when 3 wood intersects
DONE-DOCS
  DONE-explain parameterization
DONE-NON PERFECT TIMBER
  DONE-do mortise and tenon joint test with round timebr to see wha thappen lol
  DONE-add round timber stuff
  DONE-add asserts to joint functions to indicate if they can support non perfect timbers
DONE-fix accessory export
DONE-add 3mf or some other format better than STL for combined
DONE-add settings savigns
  -test it
DONE-round mortise and tenon
DONE-update kumiki should also recopy the docs folder
DONE?-clicking on patterns opens both the pattern and pattern book on my new mac...
DONE-click on patterns holud lawys open in existing collumn by default, not in an ew collumn ugh
DONE-2 kigumi version files
DONE-fix layers view in kigumi covering title
  the layers view (wher ethe joint and timber list is shown) should have the following changes:
    1. it should show up below the "title/selection" bar at the top left corner, right now it overlaps it.
    2. it should be default collapsed
    3. in it collapsed state, it should look something like []> timber list]
DONE/IGNORE-it would be better just to have agent point to docs files inside kigumi libarry, you can also add a view docs vscode command


DONE-UI whatever
  DONE-output proper error if you try and open a non-frame file
  DONE-render kumiki should not open in new tab
  IGNORE-move focus button, make it more cute
  IGNORE-add # joints table view at bottom (shouldh include joint type, but you need metadat for this)
  DONE/IGNORE-maybe add "assume upright structure" to limit camera movement with mouse?
  
  DONE-background seleciton should also change font color in viewer
  DONE-blueprint background sucks lololol blueprint should do infinite blueprint plane with horizon instead (horizon backgrounds)
  DONE-add toggle for  debug info in lower right
  DONE-selected info should just be one line
  IGNORE-add front facing direction3d to Frame
  DONE-add some background color choices (colors, gradient,s whatever fun stuff)
DONE-remove tag pill box from search
DONE-fix joint indentatino level
DONE-fix patternbook joints not showing
DONE-do remaining ntoching config classes
DONE-outline final joint requirements
DONE-do another perfermance pass on silly shed, still taking way too long
DONE-BUG timbers getting renedred incorrectly
DONE-oscare shed and tiny house not getting picked up by frame scanning
DONE-maybe qualify get_size -> get_perfect_size

DONE-add a new integration test that  creates a new kigumi projcet in some temp directory and initializes it and checks
  -kumiki patterns load correctly
  -workspace patterns load correctly
  -workspace frames load correctly
  -initialize button state is not init and then initi after initializing

DONE-PARAMETERIZATION
  DONE-do create_frame(paremteers) thing so that parameters can be tuned in UI
  DONE-do we want update generic timber creation function to take more optional parameters? could be one generic struct
    -size
    -length
    -type (round + diameter, square + nominal extra, perfect)
  DONE-pattern parameterization
    DONE-update example_shaving ctors to take some additional parameters
    -then you can add notch variations to patterns I guess?


DONE-PATTERN VIEWER
  DONE-pull out pattern scanning functionality into librarian.py (workspace, dependencies, and kumiki) and it should return a patternbook and list of frames / examples
  DONE-allow opening groups of patterns hsaring the same tag as one unit (group by tag, instead of group by pattern book)
  DONE-default shows patterns rather than pattern books
  

DONE-NOTCHING
  DONE-I think receiving timber notching is mostly good?
  DONE-notching alone not good enough, need to also chamfer out the sides of the butt timber as well 
  DONE-test notching you just added to the half dovetail joint
  DONE-fix notchingon half dovetail joint # TODO notching on this function works however the wedge won't fit so the wedge hole needs to be made bigger
    -you just need to clear out the receiving timber, the butting timber can block the wedge entry but TBH not ym problem but at least add a comment indicating this is the case


-EXEMPLIFY
  DONE-make a set of canonical joint configurations for each joint type to use ine xamples
  DONE/IGNORE-create all_joints.py 
    -exports all joints
  IGNORE-make examples of how to use measure/mark to set parameters in cutting functions
  DONE-make sawhores example nice (eh, maybe you need to wedged dovetail mortise and tenons... or nuki joints or whatever)
  IGNORE-write docs for at least one joint function to use as reference for the rest
  DONE-examplify 
    DONE-create some map of example name to example function map so you can dynamiaclly get to all examples
    DONE-would be cool do deep link by into functions/docs in the future...



DONE-LAYERS
  DONE-add tags to timbers and joints, as a 1 tier overlapping folder system
  DONE-lock and hide fetaures
  IGNORE-search bar, autocompletes both names and tags
  IGNORE-tag selection bar
    -has option to choose how many tags to show
    -unselected tags are grayed out, if all grayed out then show everything
    -when one tag is selected, unavaialble tags dissapear
  DONE-timbers/joints
    IGNORE-allow foldering by first tag option
    -clicknig on fol
  DONE-joints (separate group, shows up below timbers)
    IGNORE-allow foldering by first tag option?
    DONE-clicking on joint selects all timbers and accesories contained in it
    DONE-accessories only viewable as children of joints

DONE-cuttimber/cuttnig is wrong
  -each timber in a joint has ONE cutting
  -a cut timber is made from several joints by combining their cuttings

DONE-fix scribe notcihng algorithm (see todo in notching.py)
  -update cross lap joint to test it out again

DONE-rename measure to locate
  -then you can use measure to measure between two located features (which we don't actually care about, we need to measure between 2 CSG features...)
DONE-do the horseCoAD rename
  -rename code_goes_here to horse
  -change cut_ to layout_? or plan_?
  -rename create_timber to hew?
    -make sure to rename the ones in helperonis as well
    -then boards/panels can be mill_board
    -what about join/stretch/split?
      -join -> glue?
  -add from_joints -> raise function to Frame
    -have an on class member version of it to cuz its cutre that way
  -oscar shed -> tiny snack house
  -kumiki viewer -> horse watcher or barn raising? or 建前 or tatemae
IGONRE-consider doing a rename pass on validitiy checkers  
  -assert
  -check
  -is
  -does
  -contains (specific to cutcsg...)
  -are

VIEWER PREP
  DONE-BIG QUESTION, how to "highlight" a feature...
    -seems like you can flag meshes... but you noly want to flag part of the mesh?
  DONE-consider doing csg feature tree thingy
    -face: convex polygon, union (face), difference(face, face), infiniteplane?, rectangularface?
      -is_valid (checks all points are coplanar)
      -get_normal (based on dircetion of polygon points I guess)
    -linesegment: point pair, face intersection?
  DONE/IGNORE-add hash function
  DONE/IGNORE-I think tickets can be done as dictionary... or maybe there are compositoin fo classes?
    -e.g. material ticket, grain direction ticket?
  IGNORE-is there some way to make orientation/direction generic more like a measuring function?
  
DONE-M2 (reward: reimburse camera)
  DONE-VIEWER

DONE-M2.5 V0 release
  DONE-improve ticket class
  -features but no sub features
    -allow clicknig on joint parts
    -allow clicking onfaces to display which face yo uclicked on

DONE-initialize project button no longer showing
DONE-WTF? can't run automation commands
  Kigumi automation loop: partially blocked
  I could run the viewer command, but automation commands reported unmet preconditions:
  kigumi.automationOpenFileInViewer
  kigumi.automationListSessions
  Also, there is no .kigumi/logs directory yet in this workspace, so there are no automation JSONL logs to inspect from the agent side.
  In practical terms, Kigumi checks some internal prerequisites before running automation commands like automationOpenFileInViewer, automationListSessions, and automationRefreshSession. Typical prerequisites are:

  Kigumi extension is fully activated.
  A viewer session is open in the right mode.
  The current file is recognized as a renderable Kigumi pattern.
  Automation/session services are initialized for that workspace window.
  When those checks fail, VS Code reports exactly what I saw: command exists, but preconditions are not met.
DONE-restyle kigumi viewer so that there are 2 collumns for the options and the parameters panel. The refresh button on the parameters view should be at the top as well so it's easier to see.
IGNORE-rename nominal -> maximal
  -ignore for now
IGNORE-decide on notching.py vs relief.py
DONE-AUTOMATION
  DONE-allow it to scan for open kiguim windows and force refresh once changes are complete if no refresh had happened yet
  DONE-allow agent to read kigumi console output
  DONE-allow agent to take screenshot after refreshing (also allow agent to change camera angle for the screenshot)
  DONE-make file watch refresh toggleable (off by default)
  DONE-update instructions to
    -first test locally in a headless way to see that there are no errors
    -then do actula render and fetch output
  DONE-create headless runner and adress the following in instructions
    - first test locally just by running the python script directly to confirm there are no errors and the logging looks accurate
DONE-view sourec and duplicate to workspace is broken again... note that view source should show up on all frames and patterns, the duplicate to workspace option should ONLY show up on patterns found in libraries

DONE-fresh install test
    -create a temp directory and open vscode test in that directory
    -check for sidebar state before initializati
    -initilialize kigumi
    -check for sidebar state after initializati
    -check .gitignore
    -check for my frame
    -check venv is installde correctly
    -check kumiki version
    -check kumiki has correct pattern index (technically this is a kumiki publish test)
DONE-improve librarian
  -generate pattern index file for kumiki before pubishing
  -search order
    -workspace
    -kumiki dep
    -other depnedencies
  -when searching a dependency
    -only proceed if found kumiki dependency (unless kumiki itself)
      -check for pattern index
      -scan all files
  -when scanning for files
    -check for kumiki dependency and early quit if not found to save processing time
    -run type analyzer to pull out stuff with frames and patternbooks
    -if frame, just use static analyzer results to list the filename
      -if multiple frames found, show only the last one and indicate multiple frames found 
    -if patternbook, then actually load teh file to get teh patterns and sub patterns
      -optionall write to an index file which also contains file hash
DONE-reorganize agent files
DONE-rename?
  DONE-create notching.py
  DONE-build-a-butt-joint -> build-a-butt.py
  DONE-joint_shavings.py->shavings.py
BUG-joint list not shoiwng up for round timber mortise and tenon joint exapmle
DONE-internet seems to be necessary for rendering?
DONE-do image to frame experiments (some with chain of reason promptitng, some without)
DONE-improve joint writing agent instructinos
  DONE-marking space step is optional, not actually all that useful, instead clearly annotate reference features/spaces on the joint and use what's needed
  DONE-build a butt joint guidance
  DONE-notching guidance
  DONE-peg guidance
DONE/IGNORE-how dose selection change after refresh?
IGNORE-highlight timebrs in timber list with selection, or just get rid of timber list?
DONE-create workshop jonits folders
DONE-have publish package job bump the version number (check current version and bump I guess)
DONE-test the initialize project thing lol
  -need to pbulish kumiki first
DONE/IGNORE-decide on layer view
  -is patternbook part of layer view or separate thing
  -figure out how to do show hide option
  -decide on section: timbers, joints (this is fine for now)
    -how do these sections integrate with folders
  -decide on folders being string refs or actual refs when constructing...
DONE-add dovetail shoulder to build a butt Joint 
DONE-consider making all end cuts 90 degrees rather than half spaces, that was prob a bad idea
DONE-address # TODO this is wrong, just return a new EmptyCSG

DONE-kigumi
  DONE-add loading grahpic and do background loading of patterns
  DONE-add refresh button
  DONE-remove examples from sidebar or get it work right
  DONE-only scan patterns folder
  DONE-make proper header and buttons or something...
  DONE?-frames section should omit examples if it starts with "patternbook" as these are patterns
  DONE-patterns section should have a drop down that says "group by patternbook" and then cilcking on the patternbook folder opens the entire patternbook in the viewer
  DONE-clicking on paternbook group folder opens the patternbook
  DONE-allow viewing source on pattern/frames, obvious what to do in workspace patterns, for shipped pattern, it  will open a read only copy it from the dependencies folder?
    -for kumiji shipped patterns also have an option to duplicate pattern in workspace, which will make a copy of teh pattern in workspace for you to modify
    -view source/duplicate source buttons are broken right now...
  DONE?-add special mode for local development??
    -does not initilaize python env stuff and detects that it is in local dev mode
    -does not scan for kumiki (add a notice for local development, and patterns/exampes will show up as workspace patterns)
  DONE-have toggel to choso et open kigumi in separate collumn or in same tab group
  DONE-have settings menu in extension menu to change settings like open in split tab
  DONE-pattern scanning still weird
  DONE-patternbook grouping not working for workspace pattern
  DONE-frames not being detected in local dev mode?
  DONE-group by patternbook still does not have an icon
  DONE-add view source button to frames
  DONE-remove toggle split view button from sidebar, or when you click it it should turn from a single to a double square
  DONE-add integration test for sidebar
  DONE-update examples to be exmaples and not patterns
  DONE-fix examples and ptatern separation, or maybe just get rid of examples
    -remove workspace examples as these are just frames, or perhaps workspace example and frame should follow the same format?
  DONE-add header section to kigumi explorer, it should have 1 box shaped buttons
    -initialize current project, or if project is already initilized, it shows information about the initialized project (or if its a local development mode)
    -remove the "open in split view" button in the top bar 
    -remov ethe open current file in viewer button in the top bar
    -remove the initialize project button in the top bar
DONE-show proper error if you open a non pattern
DONE-publish python package
  DONE-publish to testpypi first
  -setup pypi account
  -publish to pypi
DONE-kigumi bootstrapping has 2 ways, on file open and when you hit the initialize workspace button. make sure they are consistent
DONE-rethink hierarchical feature selection, as you'll want to be able to select 2 features to measure them in the future.
  -I think we'll add a "mark to measure" feature in the viewer for measuring so don't worry about it for no
DONE-do new selection hierarchy here are the states
  -nothing selected (show everything)
  -unselected (use transparency)
  -timber selected (no sub selection) (timber_selected_color)
  -timber selected (with sub selection) 
  -tagged CSG selected (no sub selection) (tagged_csg_selected_color)
  -tagged CSG selected (with sub selection) 
  -feature selected (feature_seleccted_color)
  -if there is subselection, use a transparency on everything but the sub selection


DONE-review TestHousedDovetailButtJoint and see if it deserves a camel
DONE-fix feature issues
  DONE-come up with a better term for "named CSG"
    -how about tagged CSG
  DONE-when generating named CSG meshes, it's selecting way too much, maybe you can fix with broad phase? or explicitly test each point against the CSG rather than just using geometry + normal which I assume is what's it's donig right now... or maybe it's using the CSG name which is the same rather than using CSG hierarchy
    -I think it general you should just rewrite named CSG mesh/feature generation. 
  DONE-selecting feature still doesn't highlight properly. I suspect its related to geo generation
DONE-refactor patterns to not actually load the entire pattern
DONE-BUG basic_gooseneck pattern is broken, it's not rendering teh housing tiimber. It works fine when I opne the xample in freecad. in the viewer it shows only 1 timber when there should be 2 so it's not rendering one of the timbers for some reason ,prheaps due to a name duplication? Can you figuer out why.
DONE-fix 0 length prism rendering triangulation
DONE-BUG hrosey viewer seems to run in teh background after closing all tabs... need better cleanup routine, 
DONE-BUG sharde runners that have been stopped stil have their auto relod watcher thingy running, make suer to stop it when you close it or whatever when yo cuose the tab
DONE-BUG pattern viewer thingy does not support CSG stuff
  [tinyhouse120.py] [patterns] loadPattern error: Runner command 'raise_specific_pattern' failed: Pattern 'halfspace_cut' returned Difference, expected Frame can you lok into 
DONE-BUG doubel dildo joint on oscar shed still having z fighting issues.
DONE-get rid of the old step file generatinon thing and then figure out which dependencies are mandatory and just amke them all mandatory, ono optional deps!!
DONE-rename to kumiki/kigumi

DONE-TICKET TODO
  DONE-add reference faces to PTW ticket
    -add a post init step to PTW that does the following
      -assert they are coincident
      -for now require nominal and ptw dimensions to match on reference faces
  DONE-do basic named tagging of features on CSGs 
    -just support tagging faces on halfspace and prism
    -and passing through union/difference
  IGNORE-then must be able to reference named tags inside of joint ticket
    -then when you click on a cut timber, you need to find all joints the timber belongs to to find which features on that joint you clicked
  IGNORE-add some notes for adding broadphase so you don't forget.
DONE-add stdout makers for better profiling
DONE-add settings class for persisted settings in viewer
DONE-add unselecetd transparency option
  -add a slider option so that we can set the transparency of unselected timbers (default 40%)
  -if no timbers are selected, then everything renders as if it were selected (I believe this is current behavior)
DONE/FAIL-add test for joints using max mode or something
DONE/IGNORE-improve integration tests on the viewer. Can you think of some tests to help improve it 
  -epen a test file with a couple milestones, simple timebrs and joints
    -expect timbers show up in raw data
    -expect milestones
  -one test for each of the options to see that they work
  -can we test each of the mouse/keyboard navigation things?
DONE-unselected transparency slider should be inverted
DONE-edge rendering on unselected things should also have transparency applied
DONE-transparent rendering covers shadow and reflection and also covers feature highlights :( fix pls
DONE-probably remove hashing stuff you added
DONE-redo CSG features
  -add an optional name field to CutCSG which creates a hierarchical naming scheme e.g. for mortise and tenon joint
    -timber, top level, the entire timber is considered a named CSG
      -* unnamed csg that is the top level mortise and tenon joint, which is a difference halfspace, rectangular prism CSG
        -halfspace -> "shoulderplane"
        -rectangular prism -> "tenon"
  -each time you click, it navigates a layer deeper
  -feature mesh generation should happen in python on request, not in javascript
    -walk over existing mesh and extract points that land on the feature in question, return resulting tris
  -you still want to be able to select features
    -clicking on selected CSG raycasts and selsects named CSGs within it or features if raycast hits no named CSG
    -ctrl click goes straigh to selecting features
    -not all CSGs have features yet, in these cases, you just select the CSG itself
  -add feature mesh finding/generation/rendering stats each time you click
  -display the CSG/fetaure selection string in the info section
    -hard code in "front/back/left/right/top/bot" face by checking normal when you select a face, so if you select a face that shares a normal with these direction (locally) display it e.g. (selected: post > beam mortise and tenon joint > top end tenon > face (front))
  -keep named features, they are specifically for saw/chisel metadata where we need feature level tagging, however they aren't actually being used at the moment.
      -allow features to be labeled as saw or chisel so we can do cutting instructions
      -for saw, you need to be able to saw a triangel shape (specify 3 vertices)
DONE/IGNORE-improved CSG tagging
  DONE-allow half faces on prism
  -allow full CSG feature naming
  -allow side faces and top/bot flats to be tagged on CSG extrusion
    -would be cool to allow union faces that are coplanar... but I think you can just name 2 features when defining the cutting instructions and check that they are coplanar then
DONE-try auto authoring tests again, this time ask it to use plain_joints as an example
IGNORE-update timber creation to do eval if the expression is even alittle complex and see how honeycomb shed profiling differs
DONE-fix all the crud you have in your safe math functions, maybe just clean up to all floats...
  DONE-girrafe_* methods 
  DONE-remove fast_zero test and fast_equality test
  DONe-safe_compare to take a compare arg
  DONE-do girrafe_* for compare and have all test function call it. all test functions should just be numeric
  DONE-I guess you might want symbolic test methods for some tests?? nah
  IGNORE-cleanup transform class to always use numeric
  DONE-add docs explaining how all this shit works
DONE-do another perf pass  
DONE-add plane aligned variant of join_timbers
DONE-rename join_plane_aligned_on_place_aligned_timbers to join_plane_aligned...
  DONE-update brace example to use join_plane_aligned
  IGNORE-create some example with corner braces...
DONE-I think the meshing n/N in viewer is broken??? doesnt' match profiling times? in general displayed profiling times mismatch actual time  

DONE-splined opposing double butt joint :D (could have drawbore or key)
  -you need to create shoulder_shavings for making shoulder cuts
    -shoulder cuts should be infinite in one direction so can generate the positive and negative for both sides easily
      -I guess you need a full space cut?
DONE-see what you can do about zfighting issues
DONE/IGNORE-ui widgets need to capture mouse inputs so they don't reset selection
DONE-I think you need a 3 way joint, or at least a placeholder for it. Shachisen-tsugi (車知栓継ぎ)
DONE-seemst o freak out after a python cash, need to add error handling
DONE-disable critters or fix it.... (and turn off logging ug)

DONE-do new mouse navigation
DONE-render accessories
DONE-fix reflection rendering and make background nicer?
IGNORE-maybe background colors should inehirt from vscode theme :O
DONE-need plain_double_butt joint examlpe
IGNORE-do layout
  -pop out left tab for hierarchy
  -pop up right tab for gizmos
  -info tab at right/bottom right???
  -bottom left/center section to help you scroll further down to bottom views
    -view options can go here


DONE-decide what "vanilla" project looks like
  DONE-can we just run the cdoe and take what it returns
  DONE-or do we scan for certain patterns
DONE-new shed and irrational angles are failing
DONE-start viewer I guess?
  DONE-setup stdio
  DONE-load example
  ODNE-do basic frame rendering (as boxes) get_perfect_timber_within_bounding_box_prism
  DONE-do camera centering and navigation using 3js package I guess
DONE-add wireframe rendering
DONE-3d view
  DONE-add gizmo in upper right corner
  DONE-add focus button
DONE-move out CSS html and frontend scripts into their own thing
DONE-double/triple/quadruple butt arrangement
  -assert for butt timbers pointing in cardinal directions
  -check_face_aligned_and_cardinal checks that each butt timber is pointing in one of the 4 cardinal direction orthogonal to the receiving timber, and they should all be different
DONE-add tongue and fork joint
DONE-bugs
  -fix the error you have in one of your examples
  -fix csg test error
DONE-move all jonits over to arrangements
DONE-implement tongue and fork butt joint 
DONE-fix step output
DONE-add profiing stats
DONE-add get_perfect_size_in_direction
DONE-change nominal size to nominal half size in direction (this is kind acute actually cuz it can do diagonal size which is something you need, except you probably want get_perfect_size_in_direction)
  -maybe rename to something else
DONE-rename measure to locate
DONE-build-a-butt_joint.py
DONE-I think your hashing the sympy crap so it's taking forever LOL
  DONE-add a toggle to disable the hash check thingy
DONE-need to do perf and figure out why it takes so long....
  -add loading bar on initial load
  -add individual stats for meshing (python -> js round trip. Iguess?)
    -not ecomplexiyt of teh timber
    -write stats to a file?
DONE-be able to tell which timber you clicked on if you clicked on a single timber


DONE-M1 (reward is reimburse H2D LOL)
  DONE-address TODOS in join_timbers
  DONE-create a split timber method that takes a timebr and returns 2 timbers (you splice joint themy ourself later)
  DONE-consider making a better way to store TimberWithJoints and joints class etc...
    DONE-fix tenon joint class
    DONE-add resultant length function after applying joints computerLength(timebr, [joints])
      -get fusino 360 to render just at the rusultant length 
  DONE-add helper render functions
    DONE-extend timbers so joint can be diffed out of it (rather than bool in a long timber, just use an extra long timber when creating the original timber)
    DONE-generic boolean geometry classes
    DONE-use boolean geometery to create negative of tenon cutout timber - (timber - tenon - shoulderplane)
      FAIL-calll this method tenon cutter :D
  DONE-simple joints
    DONE-basic_corner_joint
    DONE-basic_butt_joint
    DONE-basic_splice_joint
    DONE-basic_miter_joint
  DONE-cross_joints
    DONE-basic_house_joint
    DONE-basic_cross_lap_joint
  DONE-splice_joint
    DONE-gooseneck joint 
    DONE-pick a cute one from the book... maybe a gooseneck? (cuz we need it for the double dildo splice joint where we messed up)
  DONE-butt joints
    DONE-mortise and tenon with pegs and end wedges (generic functon, not intended to be called directly) (supports N pegs)
      DONE-mortise and tenon
      DONE-draw bore mortise and tenon
      DONE-through mortise and tenon
      DONE-add offset mortise peg param (so that it tightens the joint up)
    DONE-dovetail lap joint (for the floor joists)
  DONE-make shed example (just add joints)
  DONE-corner joints
    -maybe try the fancy  one we used on the shed  Mitered and keyed lap joint  (箱相欠き車知栓仕口 Hako-aikaki-shachi-sen-shikuchi)
  DONE-brace joint :(
  -improve examples
    -agent prompt examples
    -make sawhores example nice (eh, maybe you need to wedged dovetail mortise and tenons... or nuki joints or whatever)
  -update docs
    -cleanup README
      -clean up venv/makefile/setup nonsense so its simpler...
    -cleanup comments in code and delete morenotes.md
    -generate docs
    - you need a better way to specify timber lenghts... most of the time you don't really care? It should be determined by the joint in most cases.
      - so I guess joints will resize the timber, even tusk tenon joints take a tusk length parameter, tenon joints have a tenon depth parameter etc.. 
      - i think this is how it already is, update the README to explain this
    - move joint section from morenotes into concepts.md and then delete morenotes

DONE-finish mortise and tenon joint non angle stuff and peg depth
DONE/IGNORE-clean up docs on one joint function
DONE-rename joint examples to patterns
  -there can still be a examples folder in the patterns folder
DONE-update morties and tenon to support
  DONE-do cropping on angled mortise and tenon joints
  DONE-make 3 versions
    DONE-perpendicular (simplest)
    DONE-plane aligned
    DONE-no alignment (most complicated)
      -for this one you need to define the shoulder plane a certain distance away from centerline
  DONE-do the mortise_shoulder_distance_from_centerline_or_centerplane conversion
  DONE-tenon cutting on braces seems to have broken :(
  DONE-do the generic notch function
  DONE-allow pegs to be positioned in either tenon space or mortise space via parameter
    -remind folks like +y direction depends on orientation of timber, not joint angle
  DONE-add peg orientation parameter (mortise/tenon, ccw_rotation_angle) = (tenon, 0)
  DONE-non PAT stuff
    DONE-update peg logic to assume not PAT
    DONE-mortise hole logic needs to be updated too
  DONE-TODO: peg X position is measured in the tenon length axis, whereas peg Y position is measured in the mortise length axis! This makes positioning pegs on angled braces a lot easier.
    -extract peg function into its own utility... (main tricky part is determining peg length actually, position is more clear...)
    -is this really whaty ou want? and how does this intercact with positing in mortise/tenon space I guess this is a generalized version of that...
  DONE/IGNORE-fix peg depth calculation...
DONE-CutCSG_examples -> CSG_debug_examples.py
DONE-make timber names mandatory and if not provided a random one is generated?
  -if you do random ones they need to be deterministic so that they can be remapped in the viewer
DONE-fix timberlongface methods to take timberface or assert or something
IGNORE-update oscar shed to use updated joint methods?
DONE-create_canonical_ -> create_canonical_example_example_
DONE-move create_canonical_example_ stuff intointo example_shavings.py or sometihng.
DONE-joint consturction objects
  -contains timbers, joint ends, reference faces if applicable
  -contains assertm methods for checking joint sensibility
    -or you could subclass these and assert in their ctors to make it type safe at the tradeoff of more OOP...(nah don't do it, still runtime assert etiher way)
  -contains common computations for the joint configuration
  -contains dumping groups for any additional computed data?
DONE-non FAT/PAT sohulder plane
  DONE-measure_plane_from_centerline  
  DONE-still intersect tenon on shoulderplane and use intersection point to size everything (no changes needed)
  DONE-use this point to generate the notch
    -I guess... you could orient the Z of the notch cutout box with the tenon timber... and orient the XY to be parallel to mortise length axis and perp to tenon length axis... and then cut a box out
DONE-cleanup mortise and tenon joint examples so they actually test out th edifferent parameters and use canonicla ctors
DONE-measuring changes
  DONE-address TODOs in measuring
  DONE-rename marking classes so instafe of DistanceFromFace its PointFromFace or in general <marking feature type>from<reference><constraints>
  IGNORE-add a few tests where we measure/mark together and verify the results
DONE-do the timber class refactor
DONE-fix all type checking errors
DONE-fix type checking errors again lol, and fix for all folders this time...
  -need to decide if you accept ints/floats  
DONE-3 major features
  DONE-measure functions
  DONE-refactor timber class to support multiple timber types
  DONE-add timber metadata
    IGNORE-prepare for CSG metadata as well...
DONE-actually update run_Examples to use patternbook
DONE-update cut notch helper to consider nominal size
IGNORE-consider making marking functions class member function of measurement hehe
DONE-do degrees() function and radians() function
  IGNORE-make a new angle type that wraps sympy so we can typecheck on angle types
DONE-Mitered and keyed lap joint  (箱相欠き車知栓仕口 Hako-aikaki-shachi-sen-shikuchi) using new measuring functions
  DONE-add key holes
  DONE-fix non 90 degree placement
  DONE-add non 90 examlpe D:
  DONE-fix finger prisms
  DONE-default thicknesses are wrong
  DONE-add key accessories
  DONE-fix wedge length
DONE-enable and fix the no python floats check or delete it alltogether
DONE-BUG default lap settings seems to be wrong 
DONE-BUG lap splice joint seems to cut wrongside sometime
DONE-joint reorg
  DONE-basic joints has simplified versions of all joints instead
  DONE-create basic_joints for simplified verison of alle xsiting joints
    -finish mortise and tenon basic jonit
    -create patternbook for basic jonits
  DONE-cleanup joint examples
    DONE-celaup miter joint to use your new construction function thing
    DONE-change rest of joint examples to use standard joint ctor function
DONE-maybe change cutting to have maybe_top/bottom_end_cut 
DONE-add timber size estimation function (really you want a prism BB)
  -then add frame size estimation function
DONE/IGNORE-pull out parts of mortise and tenon function, maybe move to joint_helperonis.py in particular
  -round peg support
  -peg positioning
  -finding the shoulder point/shoulder plane
DONE-BUG Difference is point on boundary is wrong, you need to check the normals of the coplanar faces, if they are the same direction, than point not on boundary, if they are opposite, then point is on boundary.
  DONE-you need to add get_outward_normal function to CSG
    DONE-for union cases, check all children, return get_outward_normal on the point for all csgs that have that point on the boundary and return the average of that
    DONE-for difference cases, first see if point is on boundary on main csg and return that if so, otherwise go through difference csgs, find get_outward_normal on all the csgs that returns true if on boundary and return the average of that (negated)
  DONE-then if point is on boundary of both main csg and difference CSG, check the outward normal, if the dot product is >0 then return false, if it is <= 0 then return true 
DONE-sympy only refactor
  -force everything to be RAtional (change Numeric type back to Exprba)
  -equality_test -> equality_test_with_fuzzy_fallback
DNOE-sympy fml
  DONE-update all equality checks to have fuzzy fallback
  DONE-update safe calls to reuse numeric check
DONE-sympy refactor fml
  DONE-update all types to be expr or float again....
  -probably add points to convert expressions to numeric
    -most likely timber positioning is fine, but all joint computations should be numeric by default
    -you can ignore this for now, your improved heuristic solves this issue...


DONE-all methods have a property `as` that returns TimberFeature 
  -TimberFeature has methods `face` `reference_end` `long_face` etc 
  -so you do long_face.as.face() to do your enum castings
DONE-LocatedTimberFeature -> MarkedTimberFeature
DONE-swap measure/mark
DONE-move out all the get_ methods in Timber.py (change into mark functions)
DONE-be consistent about get/find usage
  -get -> use when you might do an "@property" but we don't get properties so use get, virtually no computation
  -find -> requries some computation
DONE-don't forget to rename class methods in timber.py
DONE-move 
  DONE-more  the helper shit in joint_shavings to  timber_shavings unless I guess if it's really super specific to joints...
  DONE-move TODO stuff in timber into timber shavings
DONE-delete deprecated stuff in joint_shavings and replace with measuring function
DONE-get rid of CSGCut and HalfSpace CSG... omg
DONE-address TODOs in joint_shavings.py
DONE-get rid of extra long edge enums... we don't orient there..
DONE-add short edges to the enum
DONE-maybe revisit measuring naming conevntion
  -we have situations where we want to scribe (multiple timbers) but work with a feature not a distance i.e. mark_closest_point_on_centerlines
  -maybe measurements should be wrapped in an object?.... then you can just measure_face().mark() and then you would do mark_into_face().measure_from_face().distance
  -and i guess then scribe means taking multiple things to one measurement and could by polymorphic to take either local measurements or global features 
  -and we would still need something that takes multiple things to one marking (that marking might not hvae an analgous measure so we can't just scribe().mark())
  -or jsut have scribe return markings that you need to measure... that's probably better... and get rid of the current scribe functions you have...
    -it's just annoying to measure cuz then you need to specify the reference end... but I guess that's a good thing as there fewer args to the mark function!
DONE-swap measure and mark
DONE-think about how you want to make example selector and make it future proof or useful as reference
  -(example metadata, (V3) -> Frame)
DONE-cleanup where to put get_point_on_face and project_point_onto_face
  -probably move these into timber_shavings or something
DONE-CutCSG -> PotatoCSG
  -or CutCSG probably better...
DONE-Moothymoth -> Rule?
DONE-type alias SomeTimberFace = Union[TimberFace, TimberReferenceEnd, TimberLongFace]


IGNORE-transform prob does not need to be frozen...
DONE-come up with consisten import scheme....
  DONE-maybe rename Union to avoid confilct...
DONE-see if you can get rid of FaceAlignedJoinedTimberOffset with a measuring function
  DONE-implementation
  DONE?-do better testing 
DONE-double dildo joint seems to broken in fusion 360
  DONE-fusion 360 broken in general...
DONE-dovetail lap joint
DONE-allow extrude polygon to extrude in both directions
DONE-change joint cut_timbers: Tuple[CutTimber, ...] into a map so you can reference joint pieces by name rather than index
DONE-add back post joints to oscar shed
DONE-delete rhino for now
IGNORE-update fusino 360 to similar as run_examples
DONE-Update Frame to be built from joints
DONE-be consistent about V2 vs (Numeric,Numeric) tuple
DONE-I tihnk the pointing constants in rule are wrong (the comments are certainly wrong)
DONE-fix morties_and_tenon_joint_example
DONE-pull out logic into joint_helperonis.py
  -split the lap helper to do the lap cut on just 1 timber...
IGNORE-add optional key to lap helper?
DONE-gooseneck lap joint
DONE-change gooseneck to return 2 Convex CSGs or support non convex polygon extrusion CSG (errors on some functions or returns None or whatever)
DONE-rafters get added twice in oscar shed
DONE-add extrude polygon csg rendering to fusino 360
DONE-fix the 13:43:54  /Users/peter.lu/kitchen/faucet/kumiki-proto/code_goes_here/japanese_joints.py:221: UserWarning: Gooseneck joint configuration may not be sensible: Joint ends are pointing in the same direction (dot product = 1.000). For a splice joint, the ends should point in opposite directions (dot product should be -1) warning
DONE-do irrational joint test for rendering
DONE/IGNORE-static method in orientation class
DONE-fix or delete goosenecksplice example in japanese joints examples
DONE-do refactors in timber as well...
DONE-REFACTOR TIME
  DONE-create Transform class in rule
  DONE-rename helperonis to something more cute
  IGNORE-make global/local var naming consistent
    DONE-add cursor rules for this
  DONE-update places to use teh local/global stuff in Transform class


DONE-cleanup
  DONE-make it clear dircetion3d and V3 are the same, and get rid of uncessary conversions...
  DONE-rename BACK to BACKWARD (DONE: renamed FORWARD to FRONT instead for consistency)
  DONE-clean up rule tests to not use exact euality...
IGNORE-get rhino rendering working for the following joints ...
DONE-see if you can fix the transform issue in fusion360...  should be able to do as rigid joint with origin
  FAIL-in fusion 360, hwen transforming occurences (components) to their final position.. rather than setting transform2. instead can you create a rigid joint with the global origin to set the position instead. 
  DONE-just do the body move way...
DONE-freecad
  DONE-clean up the run_ freecad stuff so it's more consist and easy to switch between examples
  DONE-add accesory rendering to freecad
DONE-add basic extrude profiles to CSG
DONE-update oscar shed to use mortise and tenon joint
DONE-mortise hole calculation is incorrect please fix
DONE-add Frame class to timber.py 
  -add a check_no_floats method that checks that all the contents of the frame are using sympy rationals, NO FLOATS
  -assert on the check_no_floats method in the ctor to ensure no floats are in the frame
  -update all examples to output frames
  -update fusino360 and freecad to take a frame object
  -get rid of PartiallyCutTimber class while you're at it
DONE-rewrite all joint tests, they are all kinda messed up tbh
  DONE-miter joints
    -uncomment test when get_end_position is fixed
  DONE-just delete get_end_position and the minimal_boundary thing.
  DONE-splice joints
    -delete test relying on get_end_position
  DONE-house joint
    -add test using contains_point
  DONE-cut_basic_plain_cross_lap_joint
    -TODO

DONE-improve tests
  DONE-fix is_point_on_boundary, it's not working... maybe add some tests for it...
  DONE-buttjoints
    IGNORE-fix nearest_point_on_face_to_point_global and uncomment the test
  DONE-mortise and tenon joint
    DONE-fix the T shape orientatin, it's all fucked up right now 
    DONE-peg forward_length / stickout depth tests
    DONE-peg_depth = none case, ensure it's picking the right axis to use for computing peg depth
    DONE-peg orientation should be perpendicular to tenon timber
    DONE-test points in peg hole do not line in timber/mortise csg
    DONE-test points in peg hole do lie on the peg CSG
    DONE-test mortise depth and tenon depth using CSG test functions

  
DONE/SORTA-do the big useless test cleanup
IGNORE-cleanup render utilities, some of them seem unecessary
DONE-rule
  DONE-cleanup all the static ctor stuff, it's confusing
  DONE-create new set of orientation constants for doing timber orientation
    -facing_west/east/north/south (horizontal timbers, top face up, Z-axis rotations)
    -pointing_up/down (identity and 180° Y rotation)
    -pointing_forward/backward/left/right (vertical timbers, +X→+Z, Z-axis rotations)
      -facing_west and pointing_up are both identity orientation
      -all documented with clear canonical conventions
DONE-do mortise and tenon joint
  DONE-fix peg holes in mortise not going all the way through
  DONE-fix default peg size or whatever (maybe jsut udpate eaxmple)
DONE-fix peg rendering with wrong sides ticking out...
DONE-joint rendering
  DONE-add csg function to accessoiers
  -clean up accesory rendering in fusino360
  
DONE-add units construction
DONE-sympy stuff
  DONE-fix fuzzy equal comparison function to use the .equals function. If it returns None then you probbaly do need epsilon test thingy...
  DONE/IGNORE-ask if we can add assumptions anywhere
  DONE-keep geometries as geometries for as data for as possible before converting to expression
DONE-make sure freecad rendering works
DONE-make sure oscar shed rafter pocket works
IGNORE-add some more tests for cross lap joint
DONE-address TODOs is smiple mrotise and sitance 
IGNORE-remove LongEdge 
DONE-update tests to not use floats ever (maybe have a couple float tests in a separate file though)
DONE-rework joint classes
DONE-maybe split griaffe sad :(
DONE-rename "End joint" it's not what you think it's actulaly a splice joint, I think you should call them corner joints instead?
DONE-fix name usage consistence, name should nto be in CutTimebr just use timber name
  -also add name to ctor of timber init function
DONE-check for mutabel data 
DONE-remove mutable position/name stuff from examples and oscarshed
DONE-update all warnings to use warning.warn
DONE-prefix timber methods with _in_local_space (meaning orientation and translation not applied)
DONE-cleanup the epsilon constant stuff (when to use flot vs rational for this?? think baotu ti ab it...)
DONE-change giraffe to use render_timber_with_cuts_csg rather than do it itself
DONE-fix the venv stuff in readme, aeither add venv or get rid of it from readme.
DONE-maybe get rid of corner joint
DONE-fix prism cut rendering stuff for housing join,t noe of the transforms is wrong or fusino 360 is busted
  -specifically, when cutting teh housed timber from the housing timber, the housed timber prism isn't getting positioned right. Is this happening in fusion 360 (probbaly) or in giraffe code?
    -it's definitely a fusion 360 issue

DONE-rename FootprintLocation to footprint location Type
DONE- getInside/OutsideFace(timber, footprint)
DNOE- getFacingFace(tiber, direction)
IGNORE- create readme
DONE- cleaunp morenotes (maybe delete it, all comments should be in code)
DONE- start working on docs
DONE- axis aligned timbers
DONE- rename face aligned to face parallel timebers
DONE- 2 faces can be oriented (same normals) or opposing (opposite normals)
DONE-rename "width vector" to "plus_x_width_vector" or "plus_x_width_vector_of_created_timber"
DONE-add helper asserts that does symbolic check if ratinoal otherwise fuzzy
  -check_parallel,perpendicular
DONE- note down default orientation of timbers when created in vertical and horizontal position
  - when laying horizontal, the orientation should always be consistent such that either the width or height of the timber runs in the Z axis direction
DONE/IGNORE- update timber join functions to only care about timber axis vs faces (X/widhth axis, and Y/height axis)

IGNORE- update timebr sizing notes so its really clear which size is which axis
DONE- decide on default orientation for horizontal timebrs
DONE- decide on default orinatiton for vertical timebrs on footprint
IGNORE- update join_timber to  only take axis to align with up/down rather tha face (I guess you can add a flip180 optional parameter....)
IGNORE- cerate join_timebr_raw that takes a raw face normal 
IGNORE- you need a better system for determining timber orientation after each timber creation operation, have sensible defaults 
  IGNORE- when joining 2 vertical timberss top face always faces up by default
  IGNORE- when joniing 2 horizontal timbers, top face laways faces?
  IGNORE- can you throw in a footprint to control which side top fac faces??



:: END TODONE


:: 7/2026 learn to timber frame script

- do the shed from the timber framing book
  "learn to timber frame" was one of the first books I read when I was learning to timber frame. The second half of the book walks you through how to build "the tiny timber frame". I thought it might be fun to model this in Kumiki. 
  
  
  create 6 7x7 posts on teh inside of the footprint, one in each corner, and one in the middle of the west/east sides

  join the posts to the mudsills with mortise and tenon joints, they sholud be 1.5" thick x 6.5 inches wide and 3 inches deep they should be positioned such that there is a 1.5 inch gap between the tenon and the outside edge of the mudsill. the top of the posts should be 9'4" above the top of the mudsills. give each post names like west-center-post etc

  conect the 3 posts on the west side with a top plate, the plate sholud be 7x8 with teh 8 dimension in the Z axis, and it sholud stick out each end by 1 foot past the outside edge of the post. the top of the plate should be flush with the top of the post. do the same fro the east side

  (notice I'm not asknig it to give explicit names anymore, my previous instructions should be sufficient to have the agent name all future timbers appropriately)

  join the posts to teh top plates with mortise and tenon joints matching the ones we did between the posts and the mudsills except the tenons should be 4" deep. these ones sholud take round pegs

  (and also notice I didn't spceify the peg sizing/position, a bit more dodgy this wone but it will ilkely use the same settings from the pegs on the mudsill joints)

  connect each post pair from east to west by a tie beam. the tie beam should be 7x8 with the 8" dimension it he z axis, it should be positioned 18inches below the top of the posts

  join the tie beams to the posts with a wedged half dovetail mortise and tenon joint. it is a through tenon flush with the outside of the psots and the wedge comes from the outside

  (and again, not specifying the tenon thickness this time, the wedge and dovetail shape are configurable but I'll just let the agent pick sensible defaults here. Note in the book, the shoulder has a dovetail shelf and the joint is supported by a peg. These features currently don't exist in kumiki but will be added at some point in the future. perhaps I'll make a video for it)

  connect the corner posts to the middle posts on the west side with 4x5 girts the 5" dimension is in the Z axis, and the girt should be flush with the outside face of the posts. the top of the girt is 3' 4.5" above the top of the mudsill. do for the east side as well

  join the girts to the posts with a barefaced mortise and tenon joint meaning the tenon face is flush with the inside face of the girt. the tenon should be 1.5" thick and 4" wide and 4 inches deep into the post. All girts and rafters should be barefaced when they connect to substantially bigger posts.

  connect the corner north west corner post to the north east corner post with a 4x5 girt, the top of this girt is 3' above the top of the mudsill. DO not do the same for the south side

  (notice I didn't give more details about the girt placement like I did the first time around, it should be clear to the agent what I want from the context established by the previosu prompt)

  join the girts

  on the south side, create 2 4x5 door posts connecting the south mudsill with the south tie beam. the 4" dimension matches the 4" dimension of the girt on the other side. they should be positioned symmetrically such that the door gap is 3'6"

  join the door posts to the tie beam and sill with barefaced mortise nad tenons, only the top side is pegged.

  connect the door posts to the corner posts using girts similar to how we did on the north side.

  join the girts to the posts. the tenons going into the door post should be centered rather than bare faced.

  (I probably didn't need to tell it about the tenons going into the door posts, but better to be safe than risk wasting another prompt fixing it)

  (I'm getting a little impatient here so lets try and do all the braces and joints all at once.)

  connect each corner and mid post to the top plate and tie beam around the perimiter of the buliding with a 3x5 brace such that the distance from the inside corner to the outside corner of the brace is 30". the 5" dimension of the brace is in the wall plane. join the braces using barefaced morties and tenon joints. the mortise depth is 3.5" measured orthogonally from the mortise face. the joints should be pegged.

  Add 9 5x5 rafters. The bottom face of the rafter should intersect the outside face of the plate 3.5" below the top of the plate. 2 rafters have otuside faces flush with the ends of the top plates, 2 rafters have outside faces flushi with teh outside faces of teh posts, and the remaining rafters are spaced evenly between the rafters on top ofeth posts. The roof pitch is 45 digrees and the rafters extend beyond the plate by 18"

  join the rafters to each other with tongue and fork joint, and then house the rafters in the top plate with a housing joint

  (in the book, the tongue and fork joints is pegged, and rafters are joined to the top plates with a step lap joint which don't exist yet. I may try to implement these joints later in the video. this will do for now. )

:: 5/30 kumiki intro script

hello in this video I will show you how to get started with the kumiki timber frame design language and the kigumi viewer.
And since kumiki designs are written in code, we will be relying AI agents to write our designs, but this is not required at all.

Last year I built a shed for Oscar, our neighborhood homeless friend, let's design them a shower stall next.
to get started, we'll ask the agent to define a foot print for the structure, let's say 3'x4' with 4x4 8' posts on each inside corner of the footprint.
And of course 4x4s are actually 3.5"x3.5" so let's make sure the agent knows what we mean
"let's build an outdoor shower for oscar. create a new file and start by creating a 3x4' foot print. on the inside of corner of each footprint, add a 3.5" x 3.5" posts (called a 4x4) that is 8' tall"

BTW, there are many approaches you can take to prompt the agent, and you might find some more effective than others.

"next we'll want to join the posts using 4x6s (3.5x5.5) floor beams such that there is a 3" gap between the bottom of the beam and the floor, the 5.5 size is in the vertical axis"

Now kumiki is written with timber framing terminology, and using this terminology can help the agent better understand what you're wanting.

"on the closer 4' side (call this the front side) add a top plate above the 2 front posts and sticks out by 3 inchse on each side. Do the same for the 2 back posts, except this back post should be 3 inches lower than the front side for a slanted roof"

There are also concepts specific to kumiki, and understanding these concepts will greatly improve your effecitveness when working with the agents. Please read the concepts doc if you want to learn more or just ask the agent to explain it to you!

"finally, connect the 3' sides of the structure with 2 beams that are 1 inch below the back top plate so that they don't intersect"

Here we have the basic shape of our frame... next let's add some joinery

"join the bottom floor beamns with teh posts with mortise and tenon joints, the tenon should be 1" wide and 2" in the Z axis. the tenons should be measured from the bottom of the beams and adjacent tenons pairs need one of them offset by 2 inches so that th etenons don't intersect inside the posts."

"attach the 2 beams to the posts and the 2 top plates to the post with mortise with pegged mortise and tenon joints"

Kumiki can only create joints that it knows how to cut. To see a list of joints, you can browse the patternbook in the kigumi sidebar.

"next we want to create a door, the door is a bit of a "b" shape and fits in the front opening of the structure, the door is made by 4 pieces of 1x4 stock. the door sits 1 inch inset from flush from the front face of the structure. The left vertical piece runs from the bottom beam to the top plate and is placed with a 1/2" gap from the left front post. the right vertical piece starts 2 inches away from the bottom beam and is 58" long. Then join the ends of the right vertical piece to the left vertical piece using 2 horizontal pieces such that the top and bottom edge of the horizontal pieces align with the top and bottom end of the right piece, thus forming a bit of a "b" shaped door"

"now cut mortise and tenon joints, the tenons are on the shorer horizontal pieces, and the tenons are 2" wide about 1/3" thick, and the tenons should be measured from the inside edge (relative to the door) of the tenon stock so they don't blow out the ends of the mortise stock"

we need to be little tricky to create hinges for the door

"to create the hinge, use round mortise and tenon joints between the left vertical piece and the bottom beam and top plate. the tenon should be 1 inch in diameter. the shoulder of the joint should be 1/4 inch away from the bottom beam / top plate so that the door has clearance when it swings"

kumiki currently has limited support for "boards" for more general purpose woodworking, we'll make the sides of the structures using this feature.

"fill the inside of the left/right/back sides of the structure with 3/4" thick horizontal boards, so the boards starts from the bottom beams and go to the top plate/beams. The boards should be about 7" wide and start flush with the bottom beam and intersect the posts by exactly 3/8". They should overlap by 1/4" for us to cut tongue and groove joints in them. Adjust the the width of the board so that they fill up exactly the space from the bottom beam to the top plate/beam. Join the boards together using tongue and groove joints, then cut house joints between the boards and the posts such that the boards will ultimately slide down into a groove in the posts holding them in place."

"finally place several 1.5x1.5 timbers orthogonal to the top plates spaced 2" apart and sticking out by 2" on the top to act as a sun shade, use house joints to hold them in place on the top plates, they should start 2 inches from the ends of the top plates so that there is plenty of wood to house them"


 

:: 7/2/2026 NOTCHING PLAN V2

per face end notching helpers: basically, end notching can be done on a per face basis, there are many relief modes. the angle one, and then one where it's completely flat for a bit followed by a rounded relief to the nominal size, etc

face aligned butt notching helpers: in face aligned case, we can once again split notching into top/bottom side of butt joint relief using various styles. in order to support some of styles in the timber framing book, these should be set so that it crops off after a certain point? (i.e. we only want to notch stuff above the tenon) <- maybe not, I think this is maybe a joint detai rather than a notching detail

non-face aligned ubtt notching helpers: in this case, as a reasonable fallback, we can use the face aligned butt notching helpers and enhance the width of the notch by the sin/cos of the angle of the timber and both long faces that the timber enters in.

dealing with inset shoulder: if inset shoulder is non trivial (always non trivial in non face/plane aligned cases, otheriwse non trivial if inset_shoulder > 0). if trivial, use regular notching algoritm, if non trivial, use special inset shoulder notching algorithm and extend to imperfect bounds of both timbers

old notching todo:


-NOTCHING 
  -do scribe notching on all joints (followup content yee)
  -make butt jonit with shoulder inset notching more generic
    -notch_length, measured in the length direction of the receiving timber
    -notch_width, always 100% the full dimension of the receiving timber
    -notch_rotation, maybe support this someday or stub it, or maybe you did it already, just be specific about which direction, make sure it's not more than 45 deg 
    -notch_style, straight walls only for now
    -determine notch depth -> resize notch prisms so there aren't any gaps in the notching but still not using infinite prisms
  -figure out inset notching.... config
    -0 degree notch up to insertion face
    -afterwards do the regular notching with varibale relief angle
    -but need to decide where to start relief angle in the case of insertion not orthogonal to a face?? uh oh
      -in this case, it's a non FAT/PAT arrangement and the notching parameters needs an additional arg to indicate distance from centerline to start notching from
        -this is fine but the distance from centerline to start notching from is semi specific to the joint so you need to create a special notching config class just for this, which is fine, yo udon't want to use the generic notching class for this type of joint anyways.
  -notching configs
    -you may need to fix jints to cut off actual parts rather thna exact, just ask gpt
    -do generic butt joint notching config (no inset) + implement on joints + test
    -do generic end joint notching config (no inset) + implement on joints + test
    -do generic splice joint noching config??


:: 7/2/2026 assemblies

each timber in each joint has an assembly constrain, these trace prism in R6 (directions and angles) that it can move. More specifically, it contains half or unbounded direction and angle in which it can move. there is currently an AssemblyFreedom that is wrong, the assembly freedom is per timber per joint, so the joint ticket should contain a map from its timbers to its freedoms. we don't expect to support rotational freedoms yet so feel free to NotImplemented for anything requiring rotational freedom specifi logic. IN the future, we may want a more complex shape in R6 rather than just half/full intervals, e.g. requires twisting and moving at the same time to move, or move firs,t then twist, then move etc, so please make some basic allowances for this, but for now we'll keep assemblies simple as just half or full intervals from 0

Assemblies on joints are added viat he ticket class, they have the assembly freedom and an order, then this needs to get thrown into an assembly solver. the assembly solver does the following

- iterate through orders from small to big
  - collect all joints for each given order
  - for each timber in that joint, we attempt to "move" the timber in one of the DOFs in its Assembly Freedom
    - order DOF derictions based on other timbers in the same order, choose directions away from the other timbers in its order and then away from timbers in higher orders, and prioritize DOFs that tend to be planar with the timbers in that order
    - for each DOF direction in that timber, attempt to move it in that direction, by the freed after boundary * dissamebly_multiplier
      - for each joint that that timber is also a part of (excluding the one we are currently in)
        - for ecah other timebr in that joint, if that timber shares the same DOF, then omit it, otherwise attempt to move the timbers in that joint in that direction 
          -"attempt to move" is a recursive algorithm, repeat this algorithm until all timebrs have been moved
            -if any timbers that got moved are of > assembly order, output a warning
            -if a loop is detected, continue up to 6 loops, after which fail saying unsolveable and try to output some info explaining why the constraint can't be solved

In the kigumi viewer, now we want to allow previewing assemblies. Add a "assembly preview timeline" at the bottom, put marks in the timeline for each order, sliding the bar then dissamebles each other one at a time,
set dissamebly_multiplier to be 1.5 and make it configurable via the options in kigumi
add an option to show the assembly timeline and have it on by default


assemblies next step

- set assmebly DOFs on all joints
- need a way to specify assembly order when cutting joints or after cutting joins
- some joints have multi step removal DOFs. make some notes on how t odo this... but I guessy ou can actually do it later


:: 5/30 kumiki setup video script
hello in this video I will show you how to get started with the kumiki timber frame design language and the kigumi viewer. 

If you're just here to see a demo of how kumiki is used, feel free to jump ahead to the stamp in the video.

To get stared with kumiki, you'll need to install VSCode and either UV or python. Once you've installed VSCode, open the extension menu and search for "kumiki" to install the kumiki extension which I already have installed here.

You're ready to get started now. In VSCode choose open folder and then create a new folder that will become your project workspace where all your kumiki designs will go.

Click the Kumiki icon in the sidebar to open the Kigumi extension.

Next hit the initialize project button which will initialize the folder you created with the required files. You only need to do this step once. If you're running into errors at this step, it may be because you did not install UV or python or some other issue. you can try installing python if you installed UV or vice versa.

The initialize project button will create several required files in your project workspace as well as a example design to help you get started.

Once you've initialized your project, the kigumi sidebar will allow you to browse you kumiki design, AKA frames, as well examples AKA patterns that ship with kumiki

lets open up the eaxmple frame that got created for us, this will launch the kigumi viewer
- here you can move the camera around
- select timbers
- look at the list of timbers and joints
- and so on
- if you scroll down, you'll see some options for the viewer, and some other features 

now lets go take a look the patterns that ship with kumiki in the sidebar.
patterns are simple frames that demo just one joint or concept

- here's a simple miter joint
- a mortise and tenon joint
- and here's a goosenceck splice joint

you can also hit the view or duplicate source button to see and modify the source code for the pattern.

I'll let you explore on your own here. Kumiki is being actively developed on so there will be many more patterns (and hence joints) to come

If there's ever a new version of kumiki, it will detect it and you can press a button to update to the latest version directly in the kigumi sidebar.

Now let's make our own design. Kumiki is a code aided design library. All kumiki frames are designed with as code. I'll make no speculations about our future here, but AI is really good at writing code so we'll use AI to generate our kumiki designs.

VSCode ships with the copilot AI coding agent, and if you sign up for an account which gives you enough free tokens to make a simple frame design. Since I ran already out of copilot tokens, I'll be using Claude. You can use any coding agent you want!

Now let's ask the agent to build us a sawhorse.

"let's make a sawhorse out of 5"x5" timbers. Start by creating two 2' parallle feet resting horizontally on the ground 4' apart"

"in the center of these feet, place posts that are 2' tall' to form teh legs of the sawhorse

"then connect the top of the posts with a 6' beam to form the back of the saw horse"

"Lastly, join the middle of the 2 posts with a stretcher to form the belly of the saw horse"



:: 6/21 WEBSITE INSTALL GUIDE

Kumiki is used with the Kigumi VSCode extension. To install Kigumi and use Kumiki do the following:

1. install [VSCode](https://code.visualstudio.com/)
2. install the [Kigumi VSCode extension](https://marketplace.visualstudio.com/items?itemName=minimaple.kigumi)
  a. kigumi is also [available on OpenVSX](https://open-vsx.org/extension/minimaple/kigumi) if you're using Cursor
  b. and also available on our [release page](https://github.com/minimapletinytools/kumiki/releases) as a .vsix file if you prefer that for some reason.
3. install either [uv](https://docs.astral.sh/uv/getting-started/installation/#__tabbed_1_1) or [python](https://www.python.org/downloads/), you can skip this step if you already have one of them installed
4. create a folder somewhere, and then in VScode do "File > Open Folder" and open that folder
  a. this folder will be your kigumi project "workspace"
5. once you open the folder, open teh kigumi sidebar for the VSCode extension menu 
  a. this is usually a vertical collumn on th eleft side of vscode 
  b. or sometimes in a little dropdown in the top of the left side panel.
  c. or use the command pallet (cmd/ctrl + shift + P) and choose "View: Show Kigumi"
6. in the Kigumi sidebar, click the "Initialize Project" button and wait a few seconds
  a. this will install kumiki and create several files in your workspace
7. once initialized, you can use the kigumi sidebar to open your project files, called frames, and browse various examples, called patterns
8. open "my_cute_frame" in the kigumi viewer from the sidebar
  a. modify my_cute_frame.py and click the refresh button to see any changes
9. if you make a new frame file, it will show up in the "frames" section in the kigumi sidebar explorer
  a. you may need to click the "refresh" button to make new files show up.

AI usage guide
1. Kumiki Frames can be hand coded or can be authored by AI
2. You can use any AI agent you want, VSCode ships with [copilot](https://github.com/features/copilot/ai-code-editor) so you can start with that
  a. the free copilot subscription can already do some work but it's slow, limited and doesn't like to work as hard
  b. you'll likely want to update the 10/mo license which can do mostly everything you want.
3. open the agent chat window and start asking it to do stuff
  "make me a cute saw horse, it contains 2 horizontal "feet" on a 1'x2' footprint, with two vertical "legs" on top of teh feet, and a horizontal beam resting on top of the 2 legs. There should also be a stretcher connecting the 2 legs in the middle. Joint everything with mortise and tenon joinery with square pegs please!"



:: 6/21 patternbook refacotr notes

DONE-patternbook refactor
  -get rid of explicit patternbook class, we will replace patterns with a hirearchical + tag system
  -at the top level, patterns are lumped either into a library or a workspace, this won't change, this is like a special top level folder that can be optionally disabled when displaying (don't worry about this part for now)
  -each individual pattern now has a "path" field which is a standard "/" path string that is scoped under the library name or workspace, so for example "corner_joints/plain_miter_joint/125_degrees" and this gets implicitly be namespace to "kumiki/corner_joints/plain_miter_joint/125_degrees". note in this caes 125_degrees in the name of the pattern rather than a folder
  -a pattern folder may share the same name as a pattern, hence a pattern may have children, tihs is useful becase we may have a "plain_miter_joint" example that has several different configuration examplesa s children for examples.
  -figure out a smart way to deal with name conflicts
  -the example files will return a list of patterns rather than a pattern book
  -tags work as before, each folder name is also considered an implicit tag
  -there are a couple special tags:
    -main: indicates the "main" example which may (or may not) have child configuration examples
    -poop: these examples are for internal development purposes only and are not shown normally unelss "poop" tag is explicitly used
    -corner/splice/butt/etc...: so each joint arragement has its own special tag
  -the kigumi sidebar now has the following display modes:
    -hirercihacl with implicit namespace
      -hierchical without implicit namespace (future featuer, don't do this one yet, just make suer it's possible or stub it out)
    -sorted by tags, by default in this mode, the tag selection list will contain "main" 
      -when "main" is in the tag selection list, the display mode is exceptional, it will show the "main" jointsa nd if they are children, the dropdown will be available and its children will show even if it does not have the "main" tag.
      -when "main" is not in the tag selection list, just display an unhirerchical list of all filtered joints, the displayed joint name should still include the path up until its first "main" parent
  -add a doc somewhere explaninig how the patternbook system works, keep it CONCISE but definitely outline all the rules, basicalyl just reformat what I wrote above and add anything that might be missing.
DONE-only show folders in the pattern viewer based on the folder path in the pattern,s don't use the files that returned the pattern, ignore those.
  
  


:: 6/20 THE BIG JOINT REORG

Do the big joint refactor, we will refactor joints to be sorted by their arrangement type rather than how they are organized right now. So the new joint files should be: 

-corner_joints
-splice_joints
-butt_joints
-multi-butt joints (double/triple/quadruple butt joints)
-cross_joints
-board_joints
-compound_joints
-free_joints
-decorative_joints
-basic_joints (same as is right now)

these all still remain in the workshop folder.

the non joint files should be moved into a "shavings" folder e.g. files like shavings.py, build-a-butt, notching, etc)

Come up with a plan first and confirm with me befor eimplementing




:: 6/9 adeline notes

-installing uv is weird
  -didn't know about running in the terminal
  -installing python is easy <- should support both python and uv
    -was python needed? not sure
  -open workspace step was confusing
-names of joints and timbers is hard to understand, but figured it out over time yay
-pattern book is hard to find

-having better axis labels
DONE-no way to update agent usage instructions
DONE-layers menu is covering the info
-very easy to confuse faces on timbers with faces on camera
-agent is having hard time understanding what the inset shoulder
-if you ask it to take a screenshot it wille xport as obj and then starts an http server LOL
-board doing scissor joints need a good way to do this... (use timebr or boards?)




:: 3/17/2026 selection / layer view

Lets add a layout view.... We want a menu in the webview part of the viewer that spans the entire left side that has an option >> expansion thingy to collapse it.

it should have no background or a light transparent background (similar to fusion 360's layer menu view)

it should contain a hirerchacial list view. so ecah time you go down alevel it indents by a bit.

For now, the list view contais the following:

- timbers
  - list of all timbers
    - list of cuttings on the timber
- joints
  - list of all joints
     - list of timber+cutting (so these are just cuttings, since cuttings belong to both joints and timber,s list the timber name as well here)
     - list af accesories belonging to the joint

cuttings have a sub hierrachy although these will never have persisted state associated with them and may not support things like hide/lock (these are feature we may want to add later)
-list of tagged CSGs on the cutting
  -list of features in the tagged CSG, 

Accessories do not have the sub hierarchy for CSG/features

we may support stuff like locking, fixing and hiding through the layers view in the future so make sure to architect it to support this

when you select an element in the viewer it should also highlight in the layers view and conversely as well
so basically there needs to be mapping sbetween th eselection manage,r layers view and main view.

Propose a refactor to suppor this.




-the layers view should not have a scroll bar OVERLAY over the existing view             
-hide the show/lock icons for now since tehy aren't implemneted




:: 4/14/2026 drawing generation coad

automatic drawing
- ensures every feature can be solved
- can tag features to be used as reference features and drawer prioritizes drawing to these features
- can tag features with pN to determine order which they get solved in. And P0 means a reference feature
- solve the feature tree such taht each feature is referenced to some other fetaure and teh entire tree is navigatable
- can tag features as no measure
- some feature are coincident (e.g. one face of a row of ortise holes are all coplanar) and only one of the needs to be drawn
  - this can be represented as a "shares plane" or whatever measure so no specila treatment neede necessarily
bonus
-in UI, can select which fetaure to use a reference to draw some other feature
  -so ecah feature has a list of features it is measured to


CoAD based feature refreenceing
we have a way to reference any feature on the cut timbers, so this will be the unit that CoAD drawing is done from

case study: double miter 2x4 with double miter on both end
- first flag one vertex and adjacent edge as p0
  - all miter angles will get mesaured to this edge by default 
- flag the opposite vertex as p1 and the remaining vertices are flagged as p∞ as we don't want to show solutions for them in the drawing
- using default drawer, we get the first set of stuff we need
- next, use custom drawer, we could generate a table referencing each miter angle in the drawing write the matching bevel angle for it into a table
- I guess we could also mark the bevel angle directly on the drawnig
- we could create a feature plane that cuts on the first miter and then use that to generate the bevel angle directly in the 3d view!



:: 3/30/2026 kumiki viewer environment

- when you open a my_structure.py file with the extension
- check the parent folder for an existing .kumiki/project.yaml file which will contain info on the python environment and the girrafecad version, otheriwse create it using the latest version available
- cerate teh python virtual environment to run runner.py in




:: V0 release TODO
DONE-finalize kigumi functions
  -add menu, menu should populate list of patterns and projects in the open folder (maybe ditch the in-viewer thing)
DONE-come up with sensible instalation strat
  -do pip package for release
  DONE-do proper python env on installing extension, maybe add installation loading screen
  -do vscode extension release
DONE-add generic miter joint
DONE/IGNORE-do the layers view for timbers and joints, don't do projects/patterns since you can put that in the menu, or maybe youshould still just do it? idk
DONE-get round drawbores working (holes not round and they drawbores get rendered at the origin)
DONE-make the initilaize kigumi prjoect an actually BUTTON rather than just ar egular text thing that you click on
DONE-add initalizing progress bar that also prevents you from double running the initialize method, in general the initilaize method should check if initailzation is already in progress
DONE-create a better default my_cute_frame (the current one does not work, calls non existing bulid_cube_frame function, instead make a super simple saw hores as teh example or somethnig
DONE-lock hide is broken
DONE-fix my cute frame, why are accessories not rendering correctl (need to fix round drawbore)
DONE-make cute frame tenons more flat and add draw bores just for fun
IGNORE-test the networkx fallback thing or remove it
DONE-selecting in viewer does not deselect in layer sview
DONE-increase contrast between background and objects
DONE-or maybe reduce global lighting so faces are more pronounced
DONE-integrate layers view with regular view
DONE-BUG joints view in layers does not show any joints for my cute frame
DONE-update my cute frame to use the python example or rupdate the example..
DONE-BUG patterns from kumiki do load in sid ebar but most can't be opened and view source/duplicate is broken
DONE-when viewing pattern books view in sidebar, you should eb abel to open the entire pattern book 
DONE-opening pattern errors should go to console. Im' not sure why it's not, so hard to get errors
DONE-indent for joints layers view is broken
DONE-fresh install not picking up patterns in kumiki library
DONE-add a command to open kigumi in the left hand side bar
DONE-create sensible kigumi commands, and remove or hide the rest with kigumiinternalignoreme or osmething
  -Kigumi: Open explorer
  -Kigumi: Open Current File in Viewer
DONE-top level "timbers" and "joints" folder text in the layers view should be bolded
DONE-set shadows to be off by default
DONE-the viewer area should be slightly smaller vertically so that it's easier to see and interact with the non-viewer area at the bottom
IGNORE/DONE-shadows broken in tinyshed120
DONE-text over dark background still needs to be fixed
IGNORE-even scrolling down a little bit shows the "scorll to top" button
  DONE-or perhaps if window is scrolled even a bit, scrolling the top view scrolls the entire window
DONE-layers view needs to be scrollable 
DONE-layers view should be IN the webview, or at least cropped to not overflow it
DONE-kigumi sidebar
  DONE-remove the rocket ship initialize kigumi buton from the top bar
  DONE-rmove the play button from teh top bar
  DONE-there seem to be 2x refresh buttons in the top bar, we only need 1
  DONE-add a current/latest kumiki version display, fetch version from https://pypi.org/project/kumiki/
  DONE-add an update kumiki button to kigumi
  DONE-add a go to website button in kigumi https://github.com/minimapletinytools/kumiki
DONE-ensure make pattern-index before each publish (maybe run as part of the bump version job)
DONE/IGNORE-BUG my_cute_frame no longer getting scanned
DONE-fix export
  DONE-add buttons
  DONE-add cadquery check and install button ugh
DONE-cleanup the bottom area for kigumi
  DONE-tighten the member list, dimenison + rought cut 
  DONE-add export button
  DONE-remove the patternbook
DONE-kigumi init
  DONE-initilaize project should also set .gitignor
  DONE-update kumiki should be grayed out if no project is initialized and it should just not show at all acutaly..
  DONE-initializing kumiki project from kigumi should also copy the concepts folder or maybe just copy the whole docs folder
  DONE-initialize kigumi project button line should show "initailizing project..." while initialization is in project. so in summary it has the following stase:
    -initialize project
    -initializing
    -kumiki dev mode
    -don't show at all (if project is detected)
DONE-make sure kigumi installs ltaest version of kumiki
DONE-finish docs
  DONE-finish README
  DONE-finish agents doc
  DONE/IGNORE-finish and remove docs folder?
DONE-viewer should show *changes detected" in teh webview and maybe offer ar efresh button, or just move the parmeters refresh thing to be more prominent
IGNORE-come up with better names fro command so its easier to find the important ones
IGNORE/DONE-searching
  DONE-show layer tags is broken
  -maybe layer tags should show in the list view at the bottom instead?
  -have a drop down list all tags thing that you can click + select rather than search?
  -searching should search for either tags or joint name (figure out a way to do this)
DONE-test installation
  DONE-raw installation of kigumi, installs deps
  DONE-create new project/design
  DONE-able to open patterns and copy them into self workspace
DONE-do a design pass
  DONE-timber list view should actually be useful
  DONE-stuff showing below the view should be cleaner/tighter 
  IGNORE-move focus button maybe
  IGNORE-make the gizmos slightly trasparent?
DONE-camera controls should be absolute? use focus to reset
DONE-add camera mode toggle
  -add a new toggle next to the "focus" button that has a little camear icon and either says "standard" or "free" default to standard. in standard mode, the y axis of the camera rotation is fixed to the Z axis of scene. In free mode it is as it is right now
DONE-remove trash structures (or ceraet a new spot for them so they don't ship with the library)
DONE-kigumi readme update
DONE-update copilot-insturctions to ponit directly to agent_usage_insturctions
DONE-make website
  make a website for kumiki/kigumi in the website folder. I added some photos in there for you to use. the website should be a super simple site that matches the usual programming library website. It should have 2 buttons, one that goes to github, and one that takes you to a how to install page. use a static page library for the website and make it work with github pages althought I will point to it from another domain at some point
  -get domain D:
DONE/IGNORE-test
  -screenshot funcitonality still broken
    -expsoe caputre screenshot command, not sure where it went
  -refresh the frame viewer command seems to be be broken
  -rename project.yaml to kigumi.yaml (no bw campatability please)
DONE-do one more install pass


:: 4/14/2026 menu view (deprecated now that we have sidebar)
So it goes as follows, the LHS has 3 sections in this order
>files
  **currently open pattern overriding frame x
  *currently open frame
  >current project folder
    >current frame (the currentyl open frame in the viewer)
    >(other frames in the directory)
  >(cached history of other projects folders you've opened recently I guess) 
>patterns
  >project patterns (in your project dirs pattern folder and its dependency patterns)
  >builtin patterns (from jonits)
  >beta patterns (from joints/beta)
>shop
  >timbers
  >joints
I guess you have a radio toggle to pick which ones to show? Or just collaps projects and patterns it's fine...
The advantage of the radio toggel is that you can remove the top level folder in teh view...


-clicking on patterns opens them in temp override viewer
-you can also right click or command cilkc to open in new tab 
  -this open up the pattern as a regular project? or maybe still temp, but without regular project open why not


:: 3/30/2026 math cleanup prompt

Please review rule.py and all math operations in the code base.

At some ponit Itried to make everything symbolic but realized that would accumluate too many symbols and make things impossibly slow so I switched back to floats but there are some hacks and stuff in the code that make thin sugly. Here's what I want

when creating timbers from literals, use symbolic math everywhere possible and if symbols get even a little too complicated, eval down to float
symbols should never exceed more than like 10 symbols, so lambify/numpy probably not necessary? stick to evalf? However creaet a giraffe_evalf function that calls evalf rather than call evalf directly. Basically I don't trust evalf and worry it will cause issues down the road so I want t obottle neck it all in my operation so I can do stuff like lambidy or timeouts if needed
remove all the timeout and weird hacky stuff I did in rule.py
use the safe_* math operations as much as possible. these check for symbols and shortcut to evalf if the symbols accumulate too much. in practice, they can always just eval down to floats.
Any suggestions in the future? I think in many cases I don't even need to try and preserve symbols. So should we create numeric_* math operations that automatically collapse everything down to floats and prefer using these in most places. For example, CUTCsg are easily supre complicated and shouldnot really attempt to accumulate floats as they aren't used as features to generate anythnig else.


:: 3/8/2025 viewer next steps
DONE-update vscode plugin to use python IPC or something
  -probably wrap in an interface s you can use websocket or stdio or whatever

DONE-triangulation in python
  -try triangulating CSG in python first, you can add more triangle data in the future but for now just need tris
  -actualy I think you want to store fetaure information as features need to be highlightable, not trias correspond to facees, you need to store edges and points separately
    -I guess after generating mesh you walk the mesh to tie back to features :( hope that's not too slow

DONE-smart refresh
  DONE-add filewatcher
  DONE-add hashing on timbers to determine which ones changed
  DONE-the runner keeps a hash map
  DONE-then only generate triangles for stuff that changed

IGNORE-doc tab support?

IGNORE-debug console lol

DONE-select support
  -select timber highlights it in 3d

DONE-navigation support
  -by default camera center is in center of focused objects, can be moved in view plane by panning
  -middle mouse pans
  -scroll wheel zooms and pans towards where mouse location is (in particular, it zooms to the mous eprojected onto the focal plane which is parallel to the screen and intersects the mouse orbit center)
  -pressing F resets focus (using lepr)
  DONE-right click drag rotates camera (or maybe just left click...)
  DONE-maybe include cube gizmo with preset views
  -ortho perspective toggle?
  DONE-must lerp between all transitions!!!

DONE-cute backgrounds
  DONE-creamy gradient background
  DONE-simple drop shadows? (you can fake this one)
  DONE-simple reflections? 
  -fun horse walking trhough background or osmething
    -still walking trhough structure
    -slow down speed and spawn rate a lot. jumps should eb more subtle

-simple list view
  -needs parent list hirearchy support
  -for now just show a list of timbers
    -double clicking on a timber focuses in on it
  -also show list of joints
    -double clicking focuses in on the joint (you probably need some joint center metadata, but you can just derive from the 2 timbers that make the joint for now)

-save as STL or STEP or whatever button

-patternbook integration 
  -patterns are loaded in a hierarchy (horesCoAd < imported libraries < user space)
  -"try me" button creates a copy of teh pattern script in user space with comments indicating that this is temporar yand ovewriting
  -"view source" button takes you to github or something? or maybe just makes a copy in user space or whatever

-bonus feature highlighting
  -clicking on a timber that is in focus highlight sthe featuer on the timber
    -umm, but yo udon't want to do this when too much is selecetd, maybe only do this if a joint or single timber is selected

-bonus, simple measurement support??
  -identify 2 features and draw a distance line between them, lines always show up above everything
  -distance lines can be clicked on which highlight the fetaures they measure from, hence features need to be selectively rendered

-bonus ortho view drawing support
  -take simple measurement support and then render a single timber in ortho view
  -you need a method to check if 2 timbers are the same or rotatinos/reflections of each other
  -maybe there is a likea. "drawing page" that lets you group timbers into pages, and then select how to orient timbers



:: 2/5/2026 CSG feature notes

CSG creates features, I think we can reasonable identify all faces and edges from intersections in our potato CSG code
After CSG gets triangulated, when we click/hover we can raycast onto that triangle
So we want to reveres map from that triangle to some feature
Face features can be uniquely identified by normal and boundaries
edge features are harder, as you can't click exactly on an edge, instead we can check proximity, however we only want to click on the edge if the triangle we clicked on a face that forms that edge. We can map to teh faec first, and then maybe in our csg logic we can find all edges on that face :O

Ok, so we only need to do the following:
-find the fetaure we want to track, this is either a primitive CSG feature or a derived one in the derived case, I think it's really only edges fromed from halfplanes and prism differences (e.g. the edges of a tenon shoulder)
-so we need CSG feature and we need to be able to send it through the CSG tree to diff away stuff from it.
-and CSG features can be primitive CSG features or derived ones. For derived features... maybe the noly derived feature we support is differences between some primitive CSG feature and the uncut timber prism :O, that seems reasoabel to me?

3/22 actual plan:

interface looks like:
`check_feature(csg, feature) -> bool`

feature are defined when constructing the CSG tree. We can limit it to the following 2 cases:
- as as an edge or face feature of a primitive
- as an edge feature in a difference/union of 2 face features

features are stored in a tree
- ecah csg has a feature field (should I just derive from the Ticket class so you can use the name field? probably not??)
- derive_fetaure(csg, feature_field) -> CSGFeature
- feature_field is CSG class specific, it can refer to child features in union/difference, in primitives it refers to the primitive geometry

the final returned csg has a ticket that points to a feature in the top level Difference(ptw, [negative_cuts]) 
- you will need to store this object now in cut timber instead of building it on the fly



:: 2025 documentation

-installation
-quick start 
-simple reference
  -simple examples of how to use each method with default parameters (axis aligned, face parallel only)
  -ALWAYS use Sympy types
-terminology
  -detailed examples explaing all cnocepts
-advanced reference
  -detailed examples outlining every parameter (generate from python files)
-appendix shit

:: 1/6/2026 metadata

-timber metadata 
  -material
  -reference face(s) 
    -used for generating drawings

-joint metadata
  -assembly order
  -assembly DOF
    -by combining order and DOF you can validate if it's possible to assemble
  -feature information (maybe just create a dict of named features for now and fill out the info later)
  -CSG metadata
    -named features


we want to generaize name/parent so we can build ahirerachy, 
there is one special distiction which is that there are Construction and Concept names, Construction maps to actual objects or are used to organize actual objects. Concepts map to things like joints and features

I guess for now just mash them all together but flag them as such. when rendering in something that doesn't support concepts, just omit them, or we can extract concepts into a sepaarte tree but use the same hierarchy (if they were parented to a construction, then just create ap roxy for the construction in the concept tree)


class TicketType(Enum):
  Folder
  Timber
  Accessory
  Board
  Joint
  etc

class Ticket
  name
  parent
  type: TicketType
  def is_type()
  def is_concept()
  def is_construction()



:: 2/24/2025 measure/mark rules

we have the following measurements
LocatedTimberFeature = Union['Point', 'Line', 'Plane', 'UnsignedPlane', 'HalfPlane', 'Space']

And a bunch of different markings which are local to timber features. 

But markings follow the general pattern

mark(measured_feature, timber, feature)

and then their measure function returns another measured_feature, they type of which should be determined from mesaured_feature and feature 

we want a set of rules to determine what feature type gets returned

:: 2/5/2026 JS<->Python interaction DREAM
viewer js calls frame manager python script that holds hores code and hot reloads the target script and runs it and stores the resulting frame. The frame manager has a websocket or whatever that communicates with js. The socket has teh following things:

- on_frame_updated(): fires when target script changes and script was rerun, if success, gives a list of CHANGED timbers names in the frame
- get_frame(): gets the entire frame CSG and metadata for rendering
- get_member(name): gets CSG and metadata for a specific timber
- raycast_feature(member/feature filter, raycast_point_location): returns feature that got clicked on

:: 1/23/2026 viewer planning notes

use three.js to implement viewer as a VScode plugin

base features:
-vscode integration (must be easy to use and integrate with some main script)
-render all timbers
-3d space navigation
-click on timber to focus and see more details
  -render gimbal to show local coordinates

additional features:
-incremental rerendering upon refresh
-timber/folder hierarchy (show/hide/collapse)
-hover over focused timber individual fetaures 
-feature rendering (e.g. render a face in a different color, render joint cuts in different color, etc)

future features:
-2d/3d drawing generation and view (add mesaurements to )
  -a good step 1 would be to show some basic measurements when focused on 1 timber
-assembly explosion


major decision points and things you'll probably need
-triangulation in python or js  (probably in js)
-CSG to mesh function
-add metadata to timbers
-add metadata to CSG/joints for features
  -this includes naming cuts and sub cuts like faces of a prism
  -TODO you probably need some feature CSG classes and then learn to intersect them for new features...
  -TODO this needs to be its own spec

for the naive version of feature highlighting:
  -CSG metadata maps to feature filter functions so that we can do the following
    -you probbaly need some kinda BVH to do this efficiently, but linear search is fine too LOL
  -hover -> raycast triangle -> find feature on timber




:: 1/23/2026 viewer and drawing notes [messy]

We want to be able to generate 2d/3d drawings and be able to highlight features in the viewer.

so the key thing we need is to be able to identify features on timbers. Thihhs comes in 2 flavors
1. when generating diagrams, it needs a list of features to create drawings from
2. when in the viewer, it needs to raycast to see which featuer your hovering over.

2. is more or less covered by 1, perhaps we need some additional data to deal with priorities or something but it should nto be a big deal.

For features when generating drawings, lets worok through some broad examples:

-round draw bore holes need their centers and diameters marked
-mortise holes need their dimensions and depth marked
-angled things can have lengths projected onto axis and/OR show actual angle
-only 1 side of parallel lines of rectangles need to be marked
-view from multiple isometric angles, each measurement need only be drawn once (do this automatically or allow user to specify which measurement goes to which view)
-measurements are specified to some reference point
  -you could auto pick reference point and ensure all referenced points can be referenced to face/edge on the timber I guess?
  -or allow user to specify which feature to reference to in the drawing
-automatic marking generation will probbaly make too many marking so maybe remove some?
-I think in general we need to make suer all features are solveable, have priority on some fetaures, 
  -but prehaps we just name some featuers and do due diligence in coding to ensure everything else is solveable

examples of features we want to mark:

-distance between 2 parallel faces
  -choose side perpendicular to these 2 faces to draw on (or with smallest dot product)


-special rules for face aligned features:
TODO


specific examples:

-mortise example:
  -mortise width and height (distance between opposing parallel faces of the mortise)
    -pick a view to draw it, either top or side, but not from the ends (could use priority system to determine which face of the timber to mark on?)
  -morties depth
    -distance between morties shoulder face and mortise bottom
  -mortise shoulder
    -side view only, distance between mortise shoulder face and timber face that the morties is on 
  -mortise position
    -distance from one side of the mortise to timber face
    -distance from one side of the mortise to the timber end (need rule to pick side, I guess just pick the closest?)
    -(HARD) if multiple mrotises in a row on a timber we may want to do interval spacing (from center) or side to side on the mortises
    -if rotated on face normal axis, mortise, then do a corner of the mortise + angles I guess?
    -if rotated on another axis then mortise depth is measured from 1 side of the 
  

So we need to design a way to 
1. mark features with names 



:: 1/11/2026 advancedtimber class

class PerfectTimberWithin(ABC)
  current timber stuff
  get_nominal_bounding_box_csg_local 
  get_perfect_timber_within_csg_local
  get_actual_csg_local -> CSG (you can make mesh a CSG class that errors if in a union or on the RHS of a difference)
  is_perfect_timber: check nominal_bounding_box is same as true box, only works if is timber

class Timber
  overrides get_nominal_bounding_box_csg_local 
class Board
  all boards are just stubby perfect timbers, this class adds a few more enums and type guards so that we can type check for board semantics on operations
class MeshTimber
  overrides get_nominal_bounding_box_csg_local and get_actual_csg_local which returns a mesh
class RoundTimber
  overrides get_nominal_bounding_box_csg_local and get_actual_csg_local which returns a cylinder
class PolyExtrusionTimber 
  overrides get_nominal_bounding_box_csg_local and get_actual_csg_local which returns a convexpolygonextrusion
class CSGTimber
  overrides get_nominal_bounding_box_csg_local and get_actual_csg_local which returns a some arbitrary CSG
    
# All TimberLike objects contain a PerfectTimberWithin but morever they Timbers semantically, which is to say Boards are not TimberLike Objects.
TimberLike = Union[Timber, MeshTimber, RoundTimber PolyExtrusinoTimber, CSGTimebr)]



:: 1/27 THE BIG SHAVING/MEASURING RENAME

-We need a better system for naming measure/mark, we have scenarios like
  -timber -> feature 
    -MARK
  -timber/feature -> distance
    -MEASURE_FROM
  -timber/feature -> feature
    -MARK_FROM
  -timber -> distance
    -MEASURE
  -timber/timber -> distance
    -MEASURE_BETWEEN
    -gauge?
  -timber/feature/timber/distance -> distance
    -MEASURE_BETWEEN
    -scribe?

So I think the general rule is 
mark -> outputs fetaure that isn't a point 
scribe -> outputs V3? (and not a Point... probably just get rid of Point? or have point be V3 or whatever?)
measure -> outputs distance
??? -> outputs direction
~gauge -> inputs 2 timbers, outputs distance~
~scribe -> inputs 2 timbers, outputs distance~


Or rather
- mark takes measurements in LOCAL space and outputs features in GLOBAL space
- measure takes features in GLOBAL space and outputs measurements relative to (LOCAL) features
- scribe takes features in LOCAL space and outputs features relative to (LOCAL) features

note we have the following

distance: always taken relative to a feature and can be converted to a feature itself
point: a special case of a feature
feature: everything is a feature at the end of the day :O


-prefixes we could use
  -measure
  -mark
  -gauge
  -scribe
  -get
  -find
  -compute
  -calculate
  -adopt (currently just used in adopt_csg which maybe should be changed to scribe_csg?)


:: 1/8/2026 reference dimension helper classes

measuring functions should look like this:

```
measure_from_some_feature(
  timber_to_reference,
  timber_to_mark,
  reference_feature_on_timber_to_reference,
  measurement_relative_to_reference_feature_on_timber_to_reference_local,
) -> measurement_in_timber_to_mark_local_space
```
for example:
```
timber_cross_section_size_from_reference_long_faces(
    timber_to_reference,
    timber_to_mark, 
    x_face: Optional[LongFace]
    y_face: Optional[LongFace]
    size: V2
) -> size_in_timber_to_mark_xy_local_space
# so (x,y) would have x in the x_face axis and/or y in the y_face axis of timber_to_reference
# only valid if these faces are adjacent
# you typically only want to specify 1 of these faces and the other one is inferred but nothing is stopping you from specifying both
```


sometimes we also want to take reference_feature_on_timber_to_mark and in this case, the returned measurement is relative to reference_feature_on_timber_to_mark. Of course this may only make sense in some cases, in particular that the feature in both cases share the same "basis".

let's make sharing the same basis concrete.

We use the feature definition in concepts.md

first declare the feature e.g. some long edge on a timber (we treat edges as infinite lines)
next restrict the basis e.g. measuring on some face (plane) of the timber
at this stage, we need to do some *magic* to determine the sign of the basis, so if we're measuring on a face of some timber from an edge, then positive is obviously measuring away from the edge onto the face.
for multidimensional basis, we always go x -> y -> z in that order
sometimes the reference timber and mark timber share a basis but have different signs, in this case, the measurement is taking in the reference timbers basis, and then returned in the mark timber basis obviously.
sometimes (often) the basis match but the measurements don't overlap in global space, in this case, project the point orthogonally from 1 basis to the other (TODO need to define this more rigorously, basis is insufficient, we are really talking about a distance in space relative to either a line (1D) or a point (2D) in a plane, in which case we can project to another plane that's parallel (or perhaps better to treat it as a plane/line in space that we intersect with another plane? nah)
or we might be takling about a measurement in an axis or plane (for sizes), in which case no project is needed

Here are some example of feature + basis 

-size on face
-distance from long edge on long face
-distance from end face on centerline
-distance from shoulder plane on centerline (this gets converted to distance from end face internally)

examples of things we want to do

-using join timbers, we want to set lateral offest such that the outside face of the created timber is inset from the outside face of the timber to be joined:
  -how do we do this, the created timber doesn't exist yet???
-position a rafter such that the bottom of of the rafter is 1 inch away from top of the beam
  -same issue as above...
-size a tenon


-measure functions
  -decide consistent naming scheme for measure helpers and document
  -some helpers to add (and use throughout)
    -find_closests_face_to_timber_end_direction
    -plane_from_offset_timber_face
    -also ask AI to see if there are any others
      -especially when it comes with all the timber end if statements
  

----------------
CLEANED UP VERSION OF GENERIC MEASURING FUNCTION

SCRIBE functions
in general we want a very generic scribe function
def scribe1D(distance, from_feature, on_timber, to_timber, from_feature_on_to_timber) (you actually need to constrain by a 1/2 DOF for this to work on edges/points)
notable challenges
-directionality of distance may not be intuitive
-how to constrain DOF on points/edges?

!!!!actually what we'd really like is something like

measure(measurement, feature, timber) -> global feature

mark(golbal feature, timber) -> measurement from local feature

:: 1/19/2026 impl methodology and helpre notes

-joint construction methodology
  -I think for most end joints you want to create a transform that defines where to compute all of the joint features from!!!
    -implemnetation detail, add to cursor rules
    -always use the look at convention, so that +x is right, +y is into the timber (looking at the face), +z is in the joint direction, 
  -try and do the same for butt joints? The challenge here is if it's not perpendicular
  -call it marking_transform?
-joint testing methodology
  -can we do something similar with joint testing? typically, we want to find some point on the face of a timber and work inwards to test various points in the joint
    -so in general, we want to create a transform on the timber and pick points relative to there to test
-variable naming methodology
  -ALWAYS prefix vars in global space with _global
  -try to laways prefix vars in local space with _local
  -unprefix vars should usually be in local space

-feature classes?
  -plane 
  -transform_on_timber

 

:: 1/16/2026 cut agent instructions

implement def chop_lap_on_timber_ends(top_lap_timber: Timber, top_lap_timber_end: TimberReferenceEnd, bottom_lap_timber: Timber, bottom_lap_timber_end: TimberReferenceEnd, top_lap_timber_face: TimberFace, lap_length: Numeric, lap_depth: Numeric, top_lap_shoulder_position_from_top_lap_shoulder_timber_end: Numeric) -> (CutCSG, CutCSG):
1. determine the end the top lap timber based on top_lap_shoulder_position_from_top_lap_shoulder_timber_end and lap_length
2. project the end position onto bottom_lap_timber
3. create half plane cuts to remove the end
4. determine the orientation of the lap based on top_lap_timber_face
5. in the axis orthogonal to top_lap_timber_face find where the 2 laps meet based on lap_deth
6. create prisms to remove the material from each timber resp
7. union with the half lap cuts we created earlier and return the CSG pair


implement cut_housed_dovetail_butt_joint

-there is no lap like the gooseneck joint
-assert that the receiving timber is orthognal
-so the shoulder of the dovetail starts right on the receiving timber and then inset by receiving_timber_shoulder_inset
-create a helper function in joiint_shavings to do the shoulder inset notice, it takes the face to notch, where along the face to notch and how wide (take center and width), how far to inset the shoulder
-the dovetail polygon is a simple trapezoidal shape
-use similar logic as we did on the gooseneck joint to cut the dovetail positive in the tenon timber and then adopt the dovetail CSG into the mortise timber to notch it out.



:: 1/6/2026 timber/board [ARCHIVE]

class hierarchy could look like this:

-thingy (position/orientation, allows converting from local to global coords)
  -block
    -timber
      -MeshTimber
      -RoundTimber
      -PolygonalTimber
    -board
  -joint accessory

-block has faces, but no concept of end/long face
-boards are lie really big stubby timbers, 
  -they have short edges instead of long edges lol.
  -they have sides instead of ends
-boards and timbers have different metadata
-obv, boards and timbers have different joint methods, although their implementations might ultimately be the same and you can use conversion functions if you want to put a timber joint on a board say



:: 1/9/2026 vscode extension 3js viewer

https://chatgpt.com/share/695e878b-da08-800c-a658-6913d1b54ee0

Main challenge:

- allow metadata to be attached to CSG... we want to do this per face which is tricky... as we don't have a good way to identify features in our CSG geometry.... I guess you mainly want "surfaces as result of this difference" or perhaps "surfaces with this normal at this position"

so the next step is acutally to do a CSG expansion

-allow contains_point and boundary to return none, as we will add shapes that can't easily be computed
-allow metadata to be defined on CSG primitives (HARD)
  -primitives define surfaces
  -surfaces must be "preserved" through unions and differences
  -come up with examples to see if this covers our use cases:
    -prism -> timber
    -diff prism -> tenon
    -half plane cut -> shoulder plane
  -would be cool to also mark edges and vertices
-I think the above is doable without embedding info in the meshes, you would just need to have a set of filters (and bounding volumes) to check if some edge/tri matches some feature and we can do this using normals and boundary checks etc
-add CutCSGMesh class and add to_csg_mesh function 
  -hard part, as we also need to add metadata to the mesh
  -I think metadata can be injected based on 


:: 12/30/2025 joint accesorry notes

### PEGS
pegs (could be round or rectangular), rectangular pegs can be inserted at an angle
class RoundPeg
class RectangularPeg:
    size1: Numeric # consistent rule of which axis this is w.r.t to the tenon timber
    size2: Numeric # consistent rule of which axis this is w.r.t to the tenon timber
    rotation: angle in degrees # clockwise rotation when looking at the peg from the insertion side
pegs are always orthogonal to the length axis of the 2 timbers, but they can go in from either side, specified 


QUESTION how to handle peg orientation. do we allow rotating in all idrections? should we make a separat function for tusk tenon  which may want the tenon running lengthwise with the mrotise timber

ANSWER: most generic version is that peg is defined somewhere in the tenon timber, lets oriented in the length axis by default. and then an orientation is applied...

SOLUTION: make a peg class with helpers...

class Peg:
  # peg is always stored in local space of a timber, so identity orientation has peg pointing in the same direction as the length axis of the timber (with the insertion end at the bottom)
  orientation: orientation
  position: V3
  size: Numeric
  shape: square|round 

class PegShape
  size
  shape

create_peg_going_into_face(timber: Timber, face: TimberLongFace, distance_from_bot: Numeric, distance_from_centerline: Numeric, peg_shape : PegShape)


## wedges
wedges are little triangle (with tip cut) pieces of wood going into the tenon end to wedge it in place. 

class wedge:
  # in local space fo the timber, a identity orientation has the point end of the wedge going in the length direction of the timber. 
  # the profile of the wedge (which looks like a triangle with the tip cut off) in the Y axis. Thus thickness is in the y axis
  # teh width of the wedge is in the X axis
  # the 0,0 point of the wedge is at the bottom center of the longer side of the triangle

  +z
   _
  / \
 /   \
/_____\ +x
   ↑
   origin


  orientation: ORientation
  position: V3
  base_width: Numeric
  tip_width: numeric
  thickness: Numeric
  length: Numeric # from base to tip of cut off triangle
  def width() # property alias for base_width


class WedgeShape:
  base_width
  tip_width
  thickness
  length

create_wedge_in_timber_end(timber, end, position, shape: WedgeShape)




:: 12/29/2025 mortise and tenon notes



this function is very generic and does a bunch of things. in particluar we want the following variants which will all call into this function;

      -mortise and tenon
      -through mortise and tenon
      -wedged mortise and tenon
      -wedged through mortise and tenon
      -fox wedge mortise and tenon
      -draw bore mortise and tenon
      -Hōzo-zashi Komisen-dome (ほぞ差し込み栓止め)
        -same as draw bore but with komisen peg,
      -tusked mortise and tenon 


for now, create the super generic 

cut_mortise_and_tenon_DEPRECATED

then do simple_mortise_and_tenon function that has no pegs or wedges

the cut_mortise_and_tenon_DEPRECATED function, it should take the following parameters

-tenon timber
-mortise timber
-tenon end (of the tenon timber)
-size (X,Y cross sectional size of tenon in local space of the tenon timber)
-tenon_length
-mortise_depth (must be >= to tenon length, if none, then it's a through mortise)
-tenon_rotation (ccw rotation of tenon, identity by default )
-wedges parameters (optional)
-peg parameters (otpional)


for implemntation, for now, assert on wedge and peg parameters being none, we will implement them later. also assert tenon_rotation is identity.


stub out the peg and wedge praameter classes below though

class SimplePegParameters
  shape: PegShape
  tenon_face: TimberLongFace # the face on the TENON timber that the pegs will be perpendicular too, only valid if tenon_rotation is identity
  peg_positions: [(Numeric, Numeric)] # first is in length axis measured from the shoulder of the tenon, the second is in the perpendicular axis measured from center
  depth: Optinoal[Numeric] # measured from mortise face where the peg goes in, none means peg goes all the way through to the other side
  length: length of the peg

class WedgeParameters
  shape: Wedge Shape
  depth: Numeric # depth of the wedge cut, note could be different than length of wedge
  width_axis: Direction3D # wedges run along this axis, so the, so when looking perpendicular to this and the length axis, you see the the trapezoidal "sides" of the wedges
  positions: [Numeric] # positions from center of the timber in the width axis
  expand_mortise: Numeric # amount to fan out the bottom of the mortise to fit the wedges, so 0 means straight sides, and X means expand both sides of the mortise bottom by X (total) from straight. 0 by default




:: 12/24/2025 reorg code notes


all core code should live in a "code_goes_here" folder
tests should exists side by side to code (not in their separate test folder)

-timber.py cotains all enums constants and types
-construction.py contains all our timber creation functions
-basic_joints.py contains all basic joint creation functions
-giraffe.py imports all files and rexports them together

don't touch fusion360, rhino, examples, oldcrap folder for now



::12/2025 BREP CUTTING OPERATIONS

make a generic B-rep cutting operations for doing most joints:

-very basic
  -shoulder plane 
    -specified by a position and a normal (infintie plane)
    -created relative to a timber end + direction
  -rectangular prism
    -easy for tenon ends
    -for mortises, do we use the joining timber to locate? Probably not,



Challenges:
- locating rectangular prisms from tenon and mortises
- infinet to finite bounds



:: 12/2025 timber joint rep and  timber length calculatino notes

-cut
  -substractive operation on timber
  -end cuts "set" the length of the timber (either extending it or shortening it) on the end it cut
    -ALTERNATIVE 
      -cut does not care, just does substractive cut. (this is kinda nice)
      -add suppor for infinite timbers (this is really nice)
      -unjointed end cuts can either 
        -be non infinte timber ends (a little ad-hoc)
        -we could use an end cut operation to size the timber (pro) (cumbersome, but most elegant)
  -if cutting on the bot (origin) end, the origin remains in the same place, so the final cut timber has a bottom point below 0
    -ALTERNATIVE: move origin to the very bottom always (ick?)

DECISION
  -timbers defined as is (with origin and length)
  -when a timber has a cut at the end, it becomes infinite in that direction
  -if it helps, you can think of timbers without an end cut as an infinite timber with a default end cut at the length
  
-joint
  -cuts on 1 ore more timbers that connect them


-Cut 
  -implementation detail (sorta)
  -happen at the CSG/brep level
  -if we do the extension thing
    -extending timber end includes cut
    -I guess could/should be used to infer timber length if we go with the "extension" method
  -TODO how are cuts defined, relative to timber origin or relative to a point/face on the timber?
    -maybe relative to timber, in which case a Cut class on its own is meaningless, maybe was well define TimberCut and then [TimberCut] for a list of cuts on a timber (at which point maybe just name it Cut)
-TimberCut
  -A timber with a series of Cuts on it
  -if we do the extension thing
    -has some face-cognition, meaning cuts happen relative to a face,  (this actualy only need)
      -only ends can be "extended", this is very much an ad-hoc operation
-Joint
  -a list of TimberCuts that form the joint
  -also includes joint accesories like draw bore,s fasteners, etc
-CutTimber
  -A timber with a list of TimberCuts?  


:: 11/2025 Reference Stuff

we want to introduce some kind of notion of "ReferencePoint/Line/Plane" for the purpose of positioning timbers relative to each other.
We'll call these classes "ReferenceFeature" 

For example:

I want to create 3 timbers like so:

      
      |
A-----|
  |   |
  |   |
  B   C  


so in this case, we want to use `join_timbers` and align it such that:
- the top face of timber A align with the top face of timber B
- the vertical position set above determines where it will connect with timber C



In general, we want to be able to take a ReferencePoint/Line/Plane of a timber and position that relative to another ReferencePoint/Line/Plane of a timber

We support the following ReferencePoint/Line/Plane of a timber

Timber Vertex  -> ReferencePoint
Timber Edge -> ReferenceLine
Timber Face -> ReferencePlane
Timber Centerline -> ReferenceLine
Timber Face + Timber Centerline -> ReferencePoint



Lets look at the current signature of join_timbers:

def join_timbers(timber1: Timber, timber2: Timber, 
                location_on_timber1: float,
                location_on_timber2: Optional[float] = None,
                lateral_offset: float = Integer(0),
                stickout: Stickout = None,  # Defaults to Stickout.nostickout()
                size: Optional[V2] = None,
                orientation_width_vector: Optional[Direction3D] = None) -> Timber:

In this case we want to be able to do the general positioning instead of just location_on_timber1

There are some limitations since we only have 1 DOF for positioning, in particular we must position against either the Reference Plane that is the top or bottom Face of timber1
In this case, the ReferenceFeature of Timber1 also carries an implicit direction argument, but perhaps we may as well imbue Reference Features with a direction vector? The direction vector must always be perpendicular to the line/plane. 
By convention, the directional vector coming out of an vertex/edge will be perpendicular to one of the faces that the vertex/edge lives on

For the ReferenceFeature of the created timber, it could be:

ReferencePoint
ReferenceLine/Plane that is parallel with ReferencePlane of timber 1




:: 11/2025 DONE Stickout

by default, join timbers joins between midline of timbers but often we want to join from the outside, so we should add modes to the stickout class like

stickoutReference1 / stickoutReference2

enum StickoutReference = CENTER_LINE | INSIDE | OUTSIDE


CENTER_LINE
joined timber
| |
||===== createdtimber
| |

INSIDE
joined timber
| |
| |===== createdtimber
| |

OUTSIDE
joined timber
| |
|====== createdtimber
| |


Also we should probbal yrename join_timbers to refere to timber1/2 as timberA/B instead